home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
smaltalk
/
manchest.lha
/
MANCHESTER
/
manchester
/
2.2
/
Three-D-Graphics.st
< prev
next >
Wrap
Text File
|
1993-07-24
|
122KB
|
4,403 lines
" NAME Three-D-Graphics
AUTHOR tph@cs.man.ac.uk
FUNCTION 3d pic editor
ST-VERSIONS 2.2
PREREQUISITES RepeatSwitchController
CONFLICTS
DISTRIBUTION world
VERSION 1.1
DATE 22 Jan 1989
SUMMARY Three-D-Graphics
contains the first release of Trevor's Three-D
object editor. This is known to work only with VI2.2 images; some
work would be required to get it up function with earlier images.
To get it going, try: ""ThreeDView openOn: Cone default"".
You should file in the RepeatSwitchController.st goodie before this
one.
There are a number of known bugs with this version; in particular,
attempting to manipulate deeply structured objects sometimes causes
the mouse to `lose' the appropriate vertex. Also, the `fill'
option doesn't always work correctly. The View/Controller
structure in extremely convoluted, and no attempt has been made to
document it (yet!).
"!
'From Smalltalk-80, Version 2.2 of July 4, 1987 on 1 November 1987 at 8:40:19 pm'!
!Dictionary methodsFor: 'accessing'!
at: key addIfAbsent: aBlock
"returns the value at the key. If key is absent, evaluates the block
and adds the result to self"
| obj |
^self at: key
ifAbsent: [obj _ aBlock value.
self add: (Association key: key value: obj).
obj]! !'From Smalltalk-80, Version 2.2 of July 4, 1987 on 1 November 1987 at 8:51:41 pm'!
!Form class methodsFor: 'instance creation'!
fromRectangle: aRectangle
| form |
form _ self extent: aRectangle extent.
form offset: aRectangle origin.
^form! !'From Smalltalk-80, Version 2.2 of July 4, 1987 on 1 November 1987 at 8:46:50 pm'!
!Number methodsFor: 'testing'!
signPositive
"Answer 1 if the receiver is greater than 0 else -1."
self >= 0 ifTrue: [^1] ifFalse: [^-1]! !'From Smalltalk-80, Version 2.2 of July 4, 1987 on 1 November 1987 at 8:13:13 pm'!
!Point methodsFor: 'converting'!
asThreeDPoint
"Answer with a ThreeDPoint with the same x and y coordinates
as the receiver, and a z coordinate of zero."
^ThreeDPoint x: self x y: self y z: 0.0! !
'From Smalltalk-80, Version 2.2 of July 4, 1987 on 1 November 1987 at 8:13:31 pm'!
!Number methodsFor: 'converting'!
asThreeDPoint
"Answer a new ThreeDPoint with the receiver as all coordinates;
often used to supply the same value in three dimensions, as with
symmetrical gridding or scaling."
^ThreeDPoint
x: self
y: self
z: self! !
Object subclass: #ThreeDPoint
instanceVariableNames: 'x y z '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDPoint comment:
'I represent a point in 3-D space. I have three instance variables
(x,y,z) representing this point. My protocols are modelled on those
of class Point.'!
!ThreeDPoint methodsFor: 'accessing'!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
^self!
x
"Answer the x coordinate."
^x!
x: xInteger
"Set the x coordinate."
x _ xInteger!
y
"Answer the y coordinate."
^y!
y: yInteger
"Set the y coordinate."
y _ yInteger!
z
"Answer the z coordinate."
^z!
z: zInteger
"Set the z coordinate."
z _ zInteger! !
!ThreeDPoint methodsFor: 'comparing'!
< aPoint
^(x < aPoint x and: [y < aPoint y]) and: [z < aPoint z]!
<= aPoint
^(x <= aPoint x and: [y <= aPoint y]) and: [z <= aPoint z]!
= aPoint
self species = aPoint species
ifTrue: [^(x = aPoint x and: [y = aPoint y]) and: [z = aPoint z]]
ifFalse: [^false]!
> aPoint
^(x > aPoint x and: [y > aPoint y]) and: [z > aPoint z]!
>= aPoint
^(x >= aPoint x and: [y >= aPoint y]) and: [z >= aPoint z]!
hash
^((x hash bitShift: 4) bitXor: (y hash bitShift: 2)) bitXor: z hash!
hashMappedBy: map
"My hash is independent of my oop."
^ self hash!
max: aPoint
^ThreeDPoint
x: (x max: aPoint x)
y: (y max: aPoint y)
z: (z max: aPoint z)!
max: maxPoint min: minPoint
^ThreeDPoint
x: (x max: minPoint x min: maxPoint x)
y: (y max: minPoint y min: maxPoint y)
z: (z max: minPoint z min: maxPoint z)!
min: aPoint
^ThreeDPoint
x: (x min: aPoint x)
y: (y min: aPoint y)
z: (z min: aPoint z)! !
!ThreeDPoint methodsFor: 'modifying'!
moveTo: aThreeDPoint
"Modify the receiver so that it co-encides with aThreeDPoint."
self x: aThreeDPoint x.
self y: aThreeDPoint y.
self z: aThreeDPoint z! !
!ThreeDPoint methodsFor: 'arithmetic'!
* scale
"Answer a new ThreeDPoint that is the product of the
receiver and scale (which is a ThreeDPoint or Number)."
| scaleThreeDPoint |
scaleThreeDPoint _ scale asThreeDPoint.
^ThreeDPoint
x: x * scaleThreeDPoint x
y: y * scaleThreeDPoint y
z: z * scaleThreeDPoint z!
+ delta
"Answer a new ThreeDPoint that is the sum of the
receiver and delta (which is a ThreeDPoint or Number)."
| deltaThreeDPoint |
deltaThreeDPoint _ delta asThreeDPoint.
^ThreeDPoint
x: x + deltaThreeDPoint x
y: y + deltaThreeDPoint y
z: z + deltaThreeDPoint z!
- delta
"Answer a new ThreeDPoint that is the difference of the
receiver and delta (which is a ThreeDPoint or Number)."
| deltaThreeDPoint |
deltaThreeDPoint _ delta asThreeDPoint.
^ThreeDPoint
x: x - deltaThreeDPoint x
y: y - deltaThreeDPoint y
z: z - deltaThreeDPoint z!
/ scale
"Answer a new ThreeDPoint that is the quotient of the
receiver and scale (which is a ThreeDPoint or Number)."
| scaleThreeDPoint |
scaleThreeDPoint _ scale asThreeDPoint.
^ThreeDPoint
x: x / scaleThreeDPoint x
y: y / scaleThreeDPoint y
z: z / scaleThreeDPoint z!
// scale
"Answer a new ThreeDPoint that is the quotient of the
receiver and scale (which is a ThreeDPoint or Number)."
| scaleThreeDPoint |
scaleThreeDPoint _ scale asThreeDPoint.
^ThreeDPoint
x: x // scaleThreeDPoint x
y: y // scaleThreeDPoint y
z: z // scaleThreeDPoint z!
abs
"Answer a new ThreeDPoint whose x, y and z are the absolute
values of the receiver's x, y and z."
^ThreeDPoint
x: x abs
y: y abs
z: z abs!
negated
"Answer a new ThreeDPoint whose x, y and z are the negated
values of the receiver's x, y and z."
^ThreeDPoint
x: x negated
y: y negated
z: z negated! !
!ThreeDPoint methodsFor: 'truncation and rounding'!
rounded
"Answer a new ThreeDPoint whose x, y and z are the rounded
values of the receiver's x, y and z."
^ThreeDPoint
x: x rounded
y: y rounded
z: z rounded!
truncated
"Answer a new ThreeDPoint whose x, y and z are the truncated
values of the receiver's x, y and z."
^ThreeDPoint
x: x truncated
y: y truncated
z: z truncated!
truncateTo: grid
"Answer a new ThreeDPoint that is the receiver's x, y and z
truncated to grid x, grid y and grid x."
(grid isKindOf: ThreeDPoint)
ifTrue: [
^ThreeDPoint
x: (x truncateTo: grid x)
y: (y truncateTo: grid y)
z: (z truncateTo: grid z)]
ifFalse: [
^ThreeDPoint
x: (x truncateTo: grid)
y: (y truncateTo: grid)
z: (z truncateTo: grid)]! !
!ThreeDPoint methodsFor: 'polar coordinates'!
r
"Answer the receiver's radius in polar coordinate system."
^(self dotProduct: self) sqrt! !
!ThreeDPoint methodsFor: 'point functions'!
angle: aThreeDPoint
"Answer with the angle (in degrees) between the vectors represented
by aThreeDPoint and the receiver."
| dotProduct |
dotProduct _ self dotProduct: aThreeDPoint.
^(dotProduct / (self r * aThreeDPoint r)) arcCos radiansToDegrees!
crossProduct: aThreeDPoint
"Answer with a ThreeDPoint representing the cross-product
of the receiver and aThreeDPoint (considered as vectors)."
^ThreeDPoint
x: (aThreeDPoint z * self y) - (aThreeDPoint y * self z)
y: (aThreeDPoint x * self z) - (aThreeDPoint z * self x)
z: (aThreeDPoint y * self x) - (aThreeDPoint x * self y)!
dist: aThreeDPoint
"Answer the distance between aThreeDPoint and the receiver."
^(aThreeDPoint - self) r!
dotProduct: aThreeDPoint
"Answer a Number that is the dot product of the receiver and
the argument, aThreeDPoint. That is, the two points are
multipled and the coordinates of the result summed."
| temp |
temp _ self * aThreeDPoint.
^temp x abs + temp y abs + temp z abs!
grid: aThreeDPoint
"Answer a new ThreeDPoint to the nearest rounded grid modules
specified by aThreeDPoint."
| newX newY newZ |
aThreeDPoint x = 0
ifTrue: [newX _ 0]
ifFalse: [newX _ x roundTo: aThreeDPoint x].
aThreeDPoint y = 0
ifTrue: [newY _ 0]
ifFalse: [newY _ y roundTo: aThreeDPoint y].
aThreeDPoint z = 0
ifTrue: [newZ _ 0]
ifFalse: [newZ _ z roundTo: aThreeDPoint z].
^ThreeDPoint
x: newX
y: newY
z: newZ!
isPerpendicularTo: aThreeDPoint
"Answer true if the vectors represented by aThreeDPoint
and the receiver are perpendicular, otherwise false."
^(self dotProduct: aThreeDPoint) = 0! !
!ThreeDPoint methodsFor: 'converting'!
asPoint
"Answer with a Point representing the x and y coordinates of
the receiver."
^x@y!
asThreeDPoint
"Answer the receiver itself."
^self! !
!ThreeDPoint methodsFor: 'coercing'!
coerce: aNumber
^ThreeDPoint
x: aNumber
y: aNumber
z: aNumber!
generality
^90! !
!ThreeDPoint methodsFor: 'transforming'!
rotateBy: rot
"Answer with a new ThreeDPoint rotated by rot."
^ThreeDPoint
x: (x * (rot at: 1)) + (y * (rot at: 4)) + (z * (rot at: 7))
y: (x * (rot at: 2)) + (y * (rot at: 5)) + (z * (rot at: 8))
z: (x * (rot at: 3)) + (y * (rot at: 6)) + (z * (rot at: 9))!
scaleBy: factor
"Answer a new ThreeDPoint scaled by factor (an
instance of ThreeDPoint or Number)."
(factor isKindOf: Number)
ifTrue: [^ThreeDPoint
x: factor * x
y: factor * y
z: factor * z]
ifFalse: [^ThreeDPoint
x: factor x * x
y: factor y * y
z: factor z * z]!
translateBy: delta
"Answer a new ThreeDPoint translated by delta (an
instance of ThreeDPoint or Number)."
(delta isKindOf: Number)
ifTrue: [^ThreeDPoint
x: delta + x
y: delta + y
z: delta + z]
ifFalse: [^ThreeDPoint
x: delta x + x
y: delta y + y
z: delta z + z]! !
!ThreeDPoint methodsFor: 'copying'!
deepCopy
"Implemented here for better performance."
^ThreeDPoint
x: x deepCopy
y: y deepCopy
z: z deepCopy!
shallowCopy
"Implemented here for better performance."
^ThreeDPoint
x: x
y: y
z: z! !
!ThreeDPoint methodsFor: 'printing'!
printOn: aStream
"The receiver prints on aStream in terms of infix notation."
x printOn: aStream.
aStream nextPut: $@.
y printOn: aStream.
aStream nextPut: $@.
z printOn: aStream!
storeOn: aStream
aStream nextPut: $(;
nextPutAll: self species name;
nextPutAll: ' x: ';
store: x;
nextPutAll: ' y: ';
store: y;
nextPutAll: ' z: ';
store: z;
nextPut: $).! !
!ThreeDPoint methodsFor: 'private'!
setX: xPoint setY: yPoint setZ: zPoint
x _ xPoint.
y _ yPoint.
z _ zPoint! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDPoint class
instanceVariableNames: ''!
!ThreeDPoint class methodsFor: 'instance creation'!
x: xInteger y: yInteger z: zInteger
"Answer a new instance of me with coordinates xInteger,
yInteger and zInteger."
^self new setX: xInteger setY: yInteger setZ: zInteger! !
Model subclass: #ThreeDObject
instanceVariableNames: 'cachedLines '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDObject comment:
'I am the abstract superclass of viewable three-dimensional objects. I
support a cached copy of the lines representing instances of my subclasses.
All ThreeDObjects can be converted to an OrderedCollection of ThreeDLines,
or an OrderedCollection of ThreeDPlanes. Also, all ThreeDObjects have
a default instance creation method.
'!
!ThreeDObject methodsFor: 'accessing'!
center
"Answer with a ThreeDPoint representing the center of
the receiver. By default, the center is the 'average' of the vertices."
| sum |
sum _ ThreeDPoint x: 0.0 y: 0.0 z: 0.0.
self vertices do: [:eachVertex | sum _ sum + eachVertex].
^sum / (self vertices size)!
findVertexNear: aPoint
"Answer with the vertex very close to aPoint (just to
overcome rounding errors). Answer nil if none are sufficiently
close."
^self vertices detect: [:each | (each dist: aPoint) < 1.0] ifNone: [^nil]!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
self subclassResponsibility!
vertices
"Answer with an OrderedCollection of ThreeDPoints representing the
vertices of the receiver. All ThreeDObjects should respond to this message."
self subclassResponsibility! !
!ThreeDObject methodsFor: 'comparing'!
= aThreeDObject
"All ThreeDObjects should implement this message."
self subclassResponsibility!
hashMappedBy: map
"My hash is independent of my oop."
^ self hash! !
!ThreeDObject methodsFor: 'testing'!
includesVertex: aVertex
"Answer true if the receiver contains a vertex at aVertex, otherwise false."
self vertices detect: [:eachVertex |
eachVertex = aVertex] ifNone: [^false].
^true! !
!ThreeDObject methodsFor: 'modifying'!
addObject: anObject
"Add anObject to the collection of objects representing the receiver."
self error: 'You cannot add to an primitive object'!
changed
"The model has changed, so delete the cached copy of the
lines representing the model."
cachedLines _ nil.
super changed!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint. All
ThreeDObjects should respond to this message."
self subclassResponsibility!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint. All ThreeDObjects should respond to
this message."
self subclassResponsibility!
removeObject: vertex
"Remove the object containing vertex, but only if it is not the
sole object forming the model. This method is overridden by
ThreeDModel."
self error: 'You cannot remove the entire model'! !
!ThreeDObject methodsFor: 'converting'!
asLines
"All ThreeDObjects can be converted into an OrderedCollection of ThreeDLines."
self subclassResponsibility!
asPlanes
"All ThreeDObjects can be converted into an OrderedCollection of ThreeDPlanes."
self subclassResponsibility! !
!ThreeDObject methodsFor: 'copying'!
copy
^self deepCopy! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDObject class
instanceVariableNames: ''!
!ThreeDObject class methodsFor: 'instance creation'!
default
"Answer with an instance of the receiver, with default size and
position. All ThreeDObjects should respond to this message."
self subclassResponsibility! !
ThreeDObject subclass: #ThreeDLine
instanceVariableNames: 'start end '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDLine comment:
'I represent a straight line the 3-D. My two instance variables,
''start'' and ''end'' are ThreeDPoints.'!
!ThreeDLine methodsFor: 'accessing'!
end
"Answer with a ThreeDPoint representing the end of the receiver."
^end!
end: aThreeDPoint
"Set the ThreeDPoint representing the end of the receiver."
end _ aThreeDPoint!
length
"Answer with the length of the receiver."
^(start - end) r!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
^self start!
start
"Answer with a ThreeDPoint representing the start of the receiver."
^start!
start: aThreeDPoint
"Set the ThreeDPoint representing the start of the receiver."
start _ aThreeDPoint!
start: startPoint end: endPoint
"Set the start and end points of the receiver."
start _ startPoint.
end _ endPoint!
vertices
"Answer with an OrderedCollection containing the end points
of the receiver."
^OrderedCollection with: start with: end! !
!ThreeDLine methodsFor: 'testing'!
isZeroLength
"Answer whether the receiver has a zero length."
^start = end! !
!ThreeDLine methodsFor: 'comparing'!
= aThreeDLine
"Answer whether the receiver and aThreeDLine are equal."
self species = aThreeDLine species
ifTrue: [^start = aThreeDLine start and: [end = aThreeDLine end]]
ifFalse: [^false]!
hash
^start hash bitXor: end hash!
hashMappedBy: map
"My hash is independent of my oop."
^ self hash! !
!ThreeDLine methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"Move the entire line so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
start moveTo: start + delta.
end moveTo: end + delta.
self changed!
moveVertex: vertex to: newPoint
"Move the end of the line (vertex) to newPoint."
vertex == start ifTrue: [start moveTo: newPoint] ifFalse: [
vertex == end ifTrue: [end moveTo: newPoint]].
self changed! !
!ThreeDLine methodsFor: 'truncation and rounding'!
rounded
"Answer a ThreeDLine whose start and end are rounded."
^ThreeDLine start: start rounded end: end rounded!
truncated
"Answer a ThreeDLine whose start and end are truncated."
^ThreeDLine start: start truncated end: end truncated! !
!ThreeDLine methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection containing the receiver. This
method is included for compatibility."
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection with: self].
^cachedLines!
asPlanes
"Answer with an OrderedCollection containing a plane (with two
points!!) representing the receiver. This method is included for
compatibility."
^OrderedCollection with: (ThreeDPlane with: self start with: self end)! !
!ThreeDLine methodsFor: 'transforming'!
rotateBy: aRotation
"Answer a new ThreeDLine rotated by aRotation."
^ThreeDLine
start: (start rotateBy: aRotation)
end: (end rotateBy: aRotation)!
scaleBy: aThreeDPoint
"Answer a new ThreeDLine scaled by aThreeDPoint."
^ThreeDLine
start: (start scaleBy: aThreeDPoint)
end: (end scaleBy: aThreeDPoint)!
translateBy: aThreeDPoint
"Answer a new ThreeDLine translated by aThreeDPoint."
^ThreeDLine
start: (start translateBy: aThreeDPoint)
end: (end translateBy: aThreeDPoint)! !
!ThreeDLine methodsFor: 'point functions'!
grid: aThreeDPoint
"Answer with a new ThreeDLine, with the endpoints
rounded to a grid given by aThreeDPoint."
^ThreeDLine
start: (start grid: aThreeDPoint)
end: (end grid: aThreeDPoint)! !
!ThreeDLine methodsFor: 'printing'!
printOn: aStream
"The receiver prints on aStream."
aStream nextPut: $(.
start printOn: aStream.
aStream nextPutAll: ' to '.
end printOn: aStream.
aStream nextPut: $)! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDLine class
instanceVariableNames: ''!
!ThreeDLine class methodsFor: 'instance creation'!
start: startPoint end: endPoint
"Answer with a new instance of me with the start and end
points given."
^self new start: startPoint end: endPoint! !
ThreeDObject subclass: #UnitVector
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
UnitVector comment:
'I represent a ThreeDObject corresponding to the unit vector <x,y,z>. My
principle use is to provide a model for reference purposes (in views).'!
!UnitVector methodsFor: 'accessing'!
center
"Answer with a ThreeDpoint representing the center of the receiver."
^ThreeDPoint x: 0 y: 0 z: 0!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
^(ThreeDPoint x: 0 y: 0 z: 0)!
vertices
"Answer with an OrderedCollection of the vertices
represented by the receiver."
^OrderedCollection
with: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
with: (ThreeDPoint x: 1.0 y: 0.0 z: 0.0)
with: (ThreeDPoint x: 0.0 y: 1.0 z: 0.0)
with: (ThreeDPoint x: 0.0 y: 0.0 z: 1.0)!
xLine
"Answer with a ThreeDLine representing the x-axis of the receiver."
^ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 1.0 y: 0.0 z: 0.0)!
yLine
"Answer with a ThreeDLine representing the y-axis of the receiver."
^ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 0.0 y: 1.0 z: 0.0)!
zLine
"Answer with a ThreeDLine representing the z-axis of the receiver."
^ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 0.0 y: 0.0 z: 1.0)! !
!UnitVector methodsFor: 'comparing'!
= aUnitVector
"Since UnitVectors cannot be altered, they are always equal to
one another."
^aUnitVector class == UnitVector!
hashMappedBy: map
"Answer what my hash would be if oops changed according to map"
^ map newHashFor: self hash! !
!UnitVector methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"UnitVectors cannot be modified, so do nothing."!
moveVertex: vertex to: newPoint
"UnitVectors cannot be modified, so do nothing."! !
!UnitVector methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of ThreeDLines representing
the receiver."
^OrderedCollection
with: (ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 1.0 y: 0.0 z: 0.0))
with: (ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 0.0 y: 1.0 z: 0.0))
with: (ThreeDLine
start: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
end: (ThreeDPoint x: 0.0 y: 0.0 z:1.0))!
asPlanes
"Answer with an OrderedCollection of ThreeDplanes representing
the receiver."
^OrderedCollection
with: (ThreeDPlane
with: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
with: (ThreeDPoint x: 1.0 y: 0.0 z: 0.0))
with: (ThreeDPlane
with: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
with: (ThreeDPoint x: 0.0 y: 1.0 z: 0.0))
with: (ThreeDPlane
with: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
with: (ThreeDPoint x: 0.0 y: 0.0 z: 1.0))! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
UnitVector class
instanceVariableNames: ''!
!UnitVector class methodsFor: 'instance creation'!
default
"All UnitVectors are the same."
^self new! !
ThreeDObject subclass: #Parallelogram
instanceVariableNames: 'origin horiz vert '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
Parallelogram comment:
'I represent a planar parallelogram in 3-D. My instance variables:
origin <ThreeDPoint> representing the origin (reference point).
horiz <ThreeDPoint> representing one of my extents.
vert <ThreeDPoint> representing the other extent.'!
!Parallelogram methodsFor: 'accessing'!
bottomLeft
"Answer with a ThreeDPoint representing the 'bottom left' corner
of the receiver."
^vert!
bottomRight
"Answer with a ThreeDPoint representing the 'bottom right' corner
of the receiver."
^vert - origin + horiz!
horiz
"Answer with a ThreeDPoint representing the horizontal
extent of the receiver."
^horiz!
horiz: aThreeDPoint
"Set the horizontal extent of the receiver to be aThreeDPoint."
horiz _ aThreeDPoint!
origin
"Answer with a ThreeDPoint representing the origin of the receiver."
^origin!
origin: aThreeDPoint
"Set the origin of the receiver to be aThreeDPoint."
origin _ aThreeDPoint!
origin: orig horiz: h vert: v
"Set the receiver's origin horizontal and vertical locations
as given by the arguments."
origin _ orig.
vert _ v.
horiz _ h!
origin: orig horizExtent: h vertExtent: v
"Set the receiver's origin, and the horizontal and vertical extents
as given by the arguments."
origin _ orig.
vert _ v - orig.
horiz _ h - orig!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object.
In this case, the reference point is the origin"
^origin!
topLeft
"Answer with a ThreeDPoint representing the 'top left' corner
of the receiver."
^origin!
topRight
"Answer with a ThreeDPoint representing the 'top right' corner
of the receiver."
^horiz!
vert
"Answer with a ThreeDPoint representing the vertical
extent of the receiver."
^vert!
vert: aThreeDPoint
"Set the vertical extent of the receiver to be aThreeDPoint."
vert _ aThreeDPoint!
vertices
"Answer with an OrderedCollection containing the end points
of the receiver."
^OrderedCollection
with: self topLeft
with: self topRight
with: self bottomLeft
with: self bottomRight! !
!Parallelogram methodsFor: 'comparing'!
= aParallelogram
"Answer true if the receiver's species, origin, and horizontal
and vertical extents match aParallelogram's."
self species = aParallelogram species
ifTrue: [^(origin = aParallelogram origin and: [horiz = aParallelogram horiz])
and: [vert = aParallelogram vert]]
ifFalse: [^false]!
hash
^(origin hash bitXor: horiz hash) bitXor: vert hash! !
!Parallelogram methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
self horiz moveTo: horiz + delta.
self vert moveTo: vert + delta.
self origin moveTo: origin + delta.
vertex moveTo: newPoint.
self changed!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint. Re-align the receiver appropriately."
| delta |
delta _ (newPoint - vertex) / 2.
(self oppositeVertexTo: vertex) moveTo: (self oppositeVertexTo: vertex).
(self nearestVerticesTo: vertex) do: [:eachVertex |
eachVertex moveTo: eachVertex + delta].
vertex moveTo: newPoint.
self changed! !
!Parallelogram methodsFor: 'truncation and rounding'!
rounded
"Answer with a new Parallelogram whose origin, and horizontal
and vertical sizes are rounded."
^Parallelogram origin: origin rounded horiz: horiz rounded vert: vert rounded!
truncated
"Answer with a new Parallelogram whose origin, and horizontal
and vertical sizes are truncated."
^Parallelogram origin: origin truncated horiz: horiz truncated vert: vert truncated! !
!Parallelogram methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of lines representing the receiver."
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection
with: (ThreeDLine start: self topLeft end: self topRight)
with: (ThreeDLine start: self topLeft end: self bottomLeft)
with: (ThreeDLine start: self bottomLeft end: self bottomRight)
with: (ThreeDLine start: self topRight end: self bottomRight)].
^cachedLines!
asPlanes
"Answer with an OrderedCollection containing a single
ThreeDPlane representing the receiver."
^OrderedCollection with:
(ThreeDPlane
with: self topLeft with: self topRight
with: self bottomRight with: self bottomLeft)! !
!Parallelogram methodsFor: 'transforming'!
rotateBy: aRotation
"Answer a new Parallelogram rotated by aRotation."
^Parallelogram
origin: (origin rotateBy: aRotation)
horiz: (horiz rotateBy: aRotation)
vert: (vert rotateBy: aRotation)!
scaleBy: aThreeDPoint
"Answer a new Parallelogram scaled by aThreeDPoint."
^Parallelogram
origin: (origin scaleBy: aThreeDPoint)
horiz: (horiz scaleBy: aThreeDPoint)
vert: (vert scaleBy: aThreeDPoint)!
translateBy: aThreeDPoint
"Answer a new Parallelogram translated by aThreeDPoint."
^Parallelogram
origin: (origin translateBy: aThreeDPoint)
horiz: (horiz translateBy: aThreeDPoint)
vert: (vert translateBy: aThreeDPoint)! !
!Parallelogram methodsFor: 'private'!
nearestVerticesTo: vertex
"Answer with an OrderedCollection containing the two vertices
which are nearest to vertex."
(vertex = self topLeft) ifTrue: [
^OrderedCollection with: self topRight with: self bottomLeft].
(vertex = self topRight) ifTrue: [
^OrderedCollection with: self topLeft with: self bottomRight].
(vertex = self bottomLeft) ifTrue: [
^OrderedCollection with: self topLeft with: self bottomRight].
^OrderedCollection with: self topRight with: self bottomLeft!
oppositeVertexTo: vertex
"Answer with the vertex directly opposite vertex."
(vertex = self topLeft) ifTrue: [^self bottomRight].
(vertex = self topRight) ifTrue: [^self bottomLeft].
(vertex = self bottomLeft) ifTrue: [^self topRight].
^self topLeft! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
Parallelogram class
instanceVariableNames: ''!
!Parallelogram class methodsFor: 'instance creation'!
default
"The default Parallelogram is a rectangle of size 3 by 4, centered
on the origin, in the z=0 plane."
^self
origin: (ThreeDPoint x: -1.5 y: -2.0 z: 0.0)
horiz: (ThreeDPoint x: 1.5 y: -2.0 z: 0.0)
vert: (ThreeDPoint x: -1.5 y: 2.0 z: 0.0)!
origin: orig horiz: h vert: v
"Answer with a new instance of me with origin, and horizontal
and vertical size given by the arguments."
^self new origin: orig horiz: h vert: v!
origin: orig horizExtent: h vertExtent: v
"Answer with a new instance of me with origin, and horizontal
and vertical extents given by the arguments."
^self new origin: orig horizExtent: h vertExtent: v! !
ThreeDObject subclass: #Cuboid
instanceVariableNames: 'origin corner '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
Cuboid comment:
'I represent a class of objects which have cubical shape; i.e the
width, depth and height can be any size, but all angles are constrained
to be 90 degrees. My instance variables are:
origin <ThreeDPoint> representing the front, top, left corner of the instance.
corner <ThreeDPoint> representing the back, bottom, right of the instance.
My protocol is modelled on that of class Rectangle.'!
!Cuboid methodsFor: 'accessing'!
back
"Answer the position of the receiver's back vertical side."
^corner z!
bottom
"Answer the position of the receiver's bottom horizontal side."
^corner y!
center
"Answer with a ThreeDPoint representing the center of the receiver. In
this case, the center is the actual center of the Cuboid."
^(self origin + self corner) / 2!
corner
"Answer with a ThreeDPoint representing the far bottom right
corner of the receiver."
^corner!
corner: cornerPoint
"Set the far bottom right corner of the receiver."
corner _ cornerPoint!
depth
"Answer the depth of the receiver."
^corner z - origin z!
depth: widthInteger
"Change the receiver's far vertical side to make its depth
widthInteger."
corner z: origin z + widthInteger!
extent
"Answer with a ThreeDPoint representing the extent (height,
depth and width) of the receiver."
^corner - origin!
extent: extentPoint
"Set the extent (width, depth and height) of the receiver to be
extentPoint."
corner _ origin + extentPoint!
farBottomLeft
"Answer with aThreeDPoint representing the far bottom left corner
of the receiver."
^ThreeDPoint x: origin x y: corner y z: corner z!
farBottomRight
"Answer with aThreeDPoint representing the far bottom right corner
of the receiver."
^corner!
farTopLeft
"Answer with aThreeDPoint representing the far top left corner
of the receiver."
^ThreeDPoint x: origin x y: origin y z: corner z!
farTopRight
"Answer with aThreeDPoint representing the far top right corner
of the receiver."
^ThreeDPoint x: corner x y: origin y z: corner z!
front
"Answer the position of the receiver's front vertical side."
^origin z!
height
"Answer the height of the receiver."
^corner y - origin y!
height: heightInteger
"Change the receiver's bottom y to make its height heightInteger."
corner y: origin y + heightInteger!
left
"Answer the position of the receiver's left vertical side."
^origin x!
nearBottomLeft
"Answer with aThreeDPoint representing the near bottom left corner
of the receiver."
^ThreeDPoint x: origin x y: corner y z: origin z!
nearBottomRight
"Answer with aThreeDPoint representing the near bottom right corner
of the receiver."
^ThreeDPoint x: corner x y: corner y z: origin z!
nearTopLeft
"Answer with aThreeDPoint representing the near top left corner
of the receiver."
^origin!
nearTopRight
"Answer with aThreeDPoint representing the near top right corner
of the receiver."
^ThreeDPoint x: corner x y: origin y z: origin z!
origin
"Answer with a ThreeDPoint representing the origin (near top left) of
the receiver."
^origin!
origin: originPoint
"Set the origin of the receiver to originPoint."
origin _ originPoint!
origin: originPoint corner: cornerPoint
"Set the points at the near top left corner and the far bottom
right corner of the receiver."
origin _ originPoint.
corner _ cornerPoint!
origin: originPoint extent: extentPoint
"Set the point at the near top left corner of the receiver
to be originPoint and set the width,depth and height of
the receiver to be extentPoint."
origin _ originPoint.
corner _ origin + extentPoint!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
^origin!
right
"Answer the position of the receiver's right vertical side."
^corner x!
top
"Answer the position of the receiver's top horizontal side."
^origin y!
vertices
"Answer with an OrderedCollection of the vertices represented by
the receiver."
| collection |
collection _ OrderedCollection new.
collection add: self farBottomLeft.
collection add: self farBottomRight.
collection add: self farTopLeft.
collection add: self farTopRight.
collection add: self nearBottomLeft.
collection add: self nearBottomRight.
collection add: self nearTopLeft.
collection add: self nearTopRight.
^collection!
volume
"Answer the receiver's volume, the product of width, depth and height."
^self width * self height * self depth!
width
"Answer the width of the receiver."
^corner x - origin x!
width: widthInteger
"Change the receiver's right vertical side to make its width
widthInteger."
corner x: origin x + widthInteger! !
!Cuboid methodsFor: 'comparing'!
= aCuboid
"Answer true if the receiver's species, origin and corner match aCuboid's."
self species = aCuboid species
ifTrue: [^origin = aCuboid origin and: [corner = aCuboid corner]]
ifFalse: [^false]!
hash
^origin hash bitXor: corner hash! !
!Cuboid methodsFor: 'testing'!
contains: aCuboid
"Answer whether the receiver is equal to aCuboid or whether aCuboid
is contained within the receiver."
^aCuboid origin >= origin and: [aCuboid corner <= corner]!
containsPoint: aThreeDPoint
"Answer whether aThreeDPoint is within the receiver."
^origin <= aThreeDPoint and: [aThreeDPoint < corner]!
intersects: aCuboid
"Answer whether aCuboid intersects the receiver anywhere."
^(origin max: aCuboid origin) < (corner min: aCuboid corner)! !
!Cuboid methodsFor: 'modifying'!
moveFarBottomLeft: newPoint
"Move the FarBottomLeft vertex to newPoint. The receiver has changed."
self origin x: newPoint x.
self corner y: newPoint y.
self corner z: newPoint z.
self changed!
moveFarBottomRight: newPoint
"Move the FarBottomRight vertex to newPoint. The receiver has changed."
self corner moveTo: newPoint.
self changed!
moveFarTopLeft: newPoint
"Move the FarTopLeft vertex to newPoint. The receiver has changed."
self origin x: newPoint x.
self origin y: newPoint y.
self corner z: newPoint z.
self changed!
moveFarTopRight: newPoint
"Move the FarTopRight vertex to newPoint. The receiver has changed."
self corner x: newPoint x.
self origin y: newPoint y.
self corner z: newPoint z.
self changed!
moveNearBottomLeft: newPoint
"Move the NearBottomLeft vertex to newPoint. The receiver has changed."
self origin x: newPoint x.
self corner y: newPoint y.
self origin z: newPoint z.
self changed!
moveNearBottomRight: newPoint
"Move the NearBottomRight vertex to newPoint. The receiver has changed."
self corner x: newPoint x.
self corner y: newPoint y.
self origin z: newPoint z.
self changed!
moveNearTopLeft: newPoint
"Move the NearTopLeft vertex to newPoint. The receiver has changed."
self origin moveTo: newPoint.
self changed!
moveNearTopRight: newPoint
"Move the NearTopRight vertex to newPoint. The receiver has changed."
self corner x: newPoint x.
self origin y: newPoint y.
self origin z: newPoint z.
self changed!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
self corner moveTo: corner + delta.
self origin moveTo: origin + delta.
vertex moveTo: newPoint.
self changed!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint, keeping the sides square to
the axes."
(vertex = self nearTopLeft) ifTrue: [^self moveNearTopLeft: newPoint].
(vertex = self nearBottomLeft) ifTrue: [
self moveNearBottomLeft: newPoint.
^vertex moveTo: self nearBottomLeft].
(vertex = self nearBottomRight) ifTrue: [
self moveNearBottomRight: newPoint.
^vertex moveTo: self nearBottomRight].
(vertex = self nearTopRight) ifTrue: [
self moveNearTopRight: newPoint.
^vertex moveTo: self nearTopRight].
(vertex = self farBottomLeft) ifTrue: [
self moveFarBottomLeft: newPoint.
^vertex moveTo: self farBottomLeft].
(vertex = self farTopLeft) ifTrue: [
self moveFarTopLeft: newPoint.
^vertex moveTo: self farTopLeft].
(vertex = self farTopRight) ifTrue: [
self moveFarTopRight: newPoint.
^vertex moveTo: self farTopRight].
(vertex = self farBottomRight) ifTrue: [^self moveFarBottomRight: newPoint].! !
!Cuboid methodsFor: 'truncation and rounding'!
rounded
"Answer a Cuboid whose origin and corner are rounded."
^Cuboid origin: origin rounded corner: corner rounded!
truncated
"Answer a Cuboid whose origin and corner are truncated."
^Cuboid origin: origin truncated corner: corner truncated! !
!Cuboid methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of lines representing the receiver."
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection new.
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self nearTopRight).
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self nearBottomLeft).
cachedLines add:
(ThreeDLine start: self nearTopRight end: self nearBottomRight).
cachedLines add:
(ThreeDLine start: self nearBottomLeft end: self nearBottomRight).
cachedLines add:
(ThreeDLine start: self farTopLeft end: self farTopRight).
cachedLines add:
(ThreeDLine start: self farTopLeft end: self farBottomLeft).
cachedLines add:
(ThreeDLine start: self farTopRight end: self farBottomRight).
cachedLines add:
(ThreeDLine start: self farBottomLeft end: self farBottomRight).
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self farTopLeft).
cachedLines add:
(ThreeDLine start: self nearBottomLeft end: self farBottomLeft).
cachedLines add:
(ThreeDLine start: self nearTopRight end: self farTopRight).
cachedLines add:
(ThreeDLine start: self nearBottomRight end: self farBottomRight)].
^cachedLines!
asPlanes
"Answer with an OrderedCollection of ThreeDPlanes representing
the receiver."
| collection |
collection _ OrderedCollection new.
collection add: (ThreeDPlane
with: self farTopLeft with: self farBottomLeft
with: self nearBottomLeft with: self nearTopLeft).
collection add: (ThreeDPlane
with: self farTopLeft with: self farTopRight
with: self nearTopRight with: self nearTopLeft).
collection add: (ThreeDPlane
with: self farTopRight with: self farBottomRight
with: self nearBottomRight with: self nearTopRight).
collection add: (ThreeDPlane
with: self farBottomRight with: self farBottomLeft
with: self nearBottomLeft with: self nearBottomRight).
collection add: (ThreeDPlane
with: self farBottomRight with: self farTopRight
with: self farTopLeft with: self farBottomLeft).
collection add: (ThreeDPlane
with: self nearBottomRight with: self nearTopRight
with: self nearTopLeft with: self nearBottomLeft).
^collection! !
!Cuboid methodsFor: 'transforming'!
scaleBy: scale
"Answer a new Cuboid scaled by scale, a ThreeDPoint or a scalar."
^Cuboid origin: origin * scale corner: corner * scale!
translateBy: factor
"Answer a new Cuboid translated by factor, a ThreeDPoint or a scalar."
^Cuboid origin: origin + factor corner: corner + factor! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
Cuboid class
instanceVariableNames: ''!
!Cuboid class methodsFor: 'instance creation'!
cube: aNumber
"Answer with a new instance of me representing a cube of
side aNumber, centered on the origin (0@0@0)."
| halfSide |
halfSide _ (aNumber / 2) asFloat.
^self
left: 0 - halfSide
right: halfSide
top: 0 - halfSide
bottom: halfSide
front: 0 - halfSide
back: halfSide!
default
"The default Cuboid is a cube of side 5, centered on the origin."
^self cube: 5!
left: leftNumber right: rightNumber top: topNumber bottom: bottomNumber front: frontNumber back: backNumber
"Answer an instance of me whose left, right, top, bottom, front
and back coordinates are determined by the arguments."
^self
origin: (ThreeDPoint x: leftNumber y: topNumber z: frontNumber)
corner: (ThreeDPoint x: rightNumber y: bottomNumber z: backNumber)!
origin: originPoint corner: cornerPoint
"Answer an instance of me whose corners (near top left and
far bottom right) are determined by the arguments."
^self new origin: originPoint corner: cornerPoint!
origin: originPoint extent: extentPoint
"Answer an instance of me whose near top left corner is originPoint
and width, depth and height is given by extentPoint."
^self new origin: originPoint extent: extentPoint!
unitCube
"Answer with a new instance of me representing a unit cube (i.e
with sides of 1), centered on the origin (0@0@0)."
^self cube: 1! !
ThreeDObject subclass: #Cone
instanceVariableNames: 'apex base '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
Cone comment:
'I represent a concrete class of cones with pologonal bases. My
instances variables are:
apex <ThreeDPoint> representing the top of the cone.
base <OrderedCollection> of <ThreeDPoint> representing the
base of the cone.
'!
!Cone methodsFor: 'initialize-release'!
initialize
base _ OrderedCollection new.! !
!Cone methodsFor: 'accessing'!
apex
"Answer with a ThreeDPoint representing the apex of
the receiver."
^apex!
apex: aThreeDPoint
"Set the apex of the receiver."
apex _ aThreeDPoint!
base
"Answer with the OrderedCollection of ThreeDPoints
representing the base of the receiver."
^base!
base: aCollection
"Set the base of the receiver to be aCollection."
base _ aCollection asOrderedCollection!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object."
^apex!
vertices
"Answer with an OrderedCollection of the vertices represented
by the receiver."
| collection |
collection _ self base copy.
collection add: self apex.
^collection! !
!Cone methodsFor: 'comparing'!
= aCone
"Answer true if the receiver's species, apex and base match aCone's."
self species = aCone species
ifTrue: [^apex = aCone apex and: [base = aCone base]]
ifFalse: [^false]!
hash
^apex hash bitXor: base hash!
hashMappedBy: map
"Answer what my hash would be if oops changed according to map"
^ map newHashFor: self hash! !
!Cone methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
self apex moveTo: delta + apex.
self base: (self base collect: [:each | each moveTo: delta + each]).
self changed!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint."
vertex == apex
ifTrue: [self apex moveTo: newPoint]
ifFalse: [
self base: (self base collect: [:each | each == vertex
ifTrue: [each moveTo: newPoint]
ifFalse: [each]])].
self changed! !
!Cone methodsFor: 'truncation and rounding'!
rounded
"Answer with a Cone whose apex and base are rounded."
^Cone
apex: apex rounded
base: (base collect: [:eachPoint | eachPoint rounded])!
truncated
"Answer with a Cone whose apex and base are truncated."
^Cone
apex: apex truncated
base: (base collect: [:eachPoint | eachPoint truncated])! !
!Cone methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of ThreeDLines representing the receiver."
| array |
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection new.
array _ base asArray.
1 to: (array size - 1) do: [ :i |
cachedLines add: (ThreeDLine start: (array at: i) end: (array at: i + 1)).
cachedLines add: (ThreeDLine start: (array at: i) end: apex)].
cachedLines add: (ThreeDLine start: array last end: array first).
cachedLines add: (ThreeDLine start: array last end: apex)].
^cachedLines!
asPlanes
"Answer with an OrderedCollection of ThreeDPlanes representing
the receiver."
| collection array |
collection _ OrderedCollection new.
array _ base asArray.
1 to: (array size - 1) do: [:i | collection add:
(ThreeDPlane with: (array at: i) with: (array at: i + 1) with: apex)].
collection add: (ThreeDPlane with: array last with: array first with: apex).
collection add: (ThreeDPlane vertices:
(OrderedCollection new addAll: array)).
^collection! !
!Cone methodsFor: 'transforming'!
translateBy: delta
"Answer a new Cone translated by delta (an
instance of ThreeDPoint or Number)."
^Cone
apex: (apex translateBy: delta)
base: (base collect: [:each | each translateBy: delta])! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
Cone class
instanceVariableNames: ''!
!Cone class methodsFor: 'instance creation'!
apex: aThreeDPoint base: aCollection
"create a new instance of me with the apex given by aThreeDPoint,
and the base given by aCollection of ThreeDPoints."
| cone |
cone _ self new initialize.
cone apex: aThreeDPoint.
cone base: aCollection.
^cone!
apex: aThreeDPoint sides: aNumber radius: rad
"Answer with a new instance of me with the apex at
aThreeDPoint, and the base given by aNumber points
of radius rad on the z=0 plane."
| collection step |
collection _ OrderedCollection new.
step _ (360 / aNumber) degreesToRadians.
0 to: aNumber - 1 do: [:count |
collection add: (ThreeDPoint
x: (step * count) sin * rad
y: (step * count) cos * rad
z: 0.0)].
^self apex: aThreeDPoint base: collection!
default
"The default Cone has a square base of side 5 on the x-y plane
centered at the origin, with an apex on the z-axis +7 units
from the base."
^Cone
apex: (ThreeDPoint x: 0 y: 0 z: 7)
base: (OrderedCollection
with: (ThreeDPoint x: 5 y: 5 z: 0)
with: (ThreeDPoint x: 5 y: -5 z: 0)
with: (ThreeDPoint x: -5 y: -5 z: 0)
with: (ThreeDPoint x: -5 y: 5 z: 0))!
new
^super new initialize! !
ThreeDObject subclass: #Parallelepiped
instanceVariableNames: 'origin horiz vert depth '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
!Parallelepiped methodsFor: 'accessing'!
depth
"Answer with a ThreeDPoint representing the depth
extent of the receiver."
^depth!
depth: aThreeDPoint
"Set the depth extent of the receiver to be aThreeDPoint."
depth _ aThreeDPoint!
farBottomLeft
"Answer with aThreeDPoint representing the far bottom left corner
of the receiver."
^vert - origin + depth!
farBottomRight
"Answer with aThreeDPoint representing the far bottom right corner
of the receiver."
^(vert - origin) + (horiz - origin) + depth!
farTopLeft
"Answer with aThreeDPoint representing the far top left corner
of the receiver."
^depth!
farTopRight
"Answer with aThreeDPoint representing the far top right corner
of the receiver."
^horiz - origin + depth!
horiz
"Answer with a ThreeDPoint representing the horizontal
extent of the receiver."
^horiz!
horiz: aThreeDPoint
"Set the horizontal extent of the receiver to be aThreeDPoint."
horiz _ aThreeDPoint!
nearBottomLeft
"Answer with aThreeDPoint representing the near bottom left corner
of the receiver."
^vert!
nearBottomRight
"Answer with aThreeDPoint representing the near bottom right corner
of the receiver."
^vert - origin + horiz!
nearTopLeft
"Answer with aThreeDPoint representing the near top left corner
of the receiver."
^origin!
nearTopRight
"Answer with aThreeDPoint representing the near top right corner
of the receiver."
^horiz!
origin
"Answer with a ThreeDPoint representing the origin of the receiver."
^origin!
origin: aThreeDPoint
"Set the origin of the receiver to be aThreeDPoint."
origin _ aThreeDPoint!
origin: orig horiz: h vert: v depth: d
"Set the receiver's origin horizontal, depth and vertical locations
as given by the arguments."
origin _ orig.
vert _ v.
horiz _ h.
depth _ d!
origin: orig horizExtent: h vertExtent: v depthExtent: d
"Set the receiver's origin horizontal, depth and vertical extents
as given by the arguments."
origin _ orig.
vert _ v - orig.
horiz _ h - orig.
depth _ d - orig!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object.
In this case, the reference point is the origin"
^origin!
vert
"Answer with a ThreeDPoint representing the vertical
extent of the receiver."
^vert!
vert: aThreeDPoint
"Set the vertical extent of the receiver to be aThreeDPoint."
vert _ aThreeDPoint!
vertices
"Answer with an OrderedCollection of the vertices represented by
the receiver."
| collection |
collection _ OrderedCollection new.
collection add: self farBottomLeft.
collection add: self farBottomRight.
collection add: self farTopLeft.
collection add: self farTopRight.
collection add: self nearBottomLeft.
collection add: self nearBottomRight.
collection add: self nearTopLeft.
collection add: self nearTopRight.
^collection! !
!Parallelepiped methodsFor: 'comparing'!
= aParallelepiped
"Answer true if the receiver's species, origin and corner match aParallelepiped's."
self species = aParallelepiped species
ifTrue: [^((origin = aParallelepiped origin
and: [vert = aParallelepiped vert])
and: [horiz = aParallelepiped horiz])
and: [depth = aParallelepiped depth]]
ifFalse: [^false]!
hash
^(origin hash bitXor: vert hash) bitXor: (horiz hash bitXor: depth hash)! !
!Parallelepiped methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
self horiz moveTo: horiz + delta.
self vert moveTo: vert + delta.
self depth moveTo: depth + delta.
self origin moveTo: origin + delta.
vertex moveTo: newPoint.
self changed!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint. Re-align the receiver appropriately."
| delta opposite nearestVertices oppositeVertices |
delta _ (newPoint - vertex) / 3.
opposite _ self oppositeVertexTo: vertex.
nearestVertices _ self nearestVerticesTo: vertex.
oppositeVertices _ self nearestVerticesTo: opposite.
nearestVertices do: [:eachVertex |
eachVertex moveTo: eachVertex + (delta * 2)].
oppositeVertices do: [:eachVertex |
eachVertex moveTo: eachVertex + delta].
vertex moveTo: newPoint.
self changed! !
!Parallelepiped methodsFor: 'truncation and rounding'!
rounded
"Answer with a new Parallelepiped whose origin, and horizontal
and vertical sizes are truncated."
^Parallelepiped
origin: origin truncated
horiz: horiz truncated
vert: vert truncated
depth: depth truncated! !
!Parallelepiped methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of lines representing the receiver."
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection new.
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self nearTopRight).
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self nearBottomLeft).
cachedLines add:
(ThreeDLine start: self nearTopRight end: self nearBottomRight).
cachedLines add:
(ThreeDLine start: self nearBottomLeft end: self nearBottomRight).
cachedLines add:
(ThreeDLine start: self farTopLeft end: self farTopRight).
cachedLines add:
(ThreeDLine start: self farTopLeft end: self farBottomLeft).
cachedLines add:
(ThreeDLine start: self farTopRight end: self farBottomRight).
cachedLines add:
(ThreeDLine start: self farBottomLeft end: self farBottomRight).
cachedLines add:
(ThreeDLine start: self nearTopLeft end: self farTopLeft).
cachedLines add:
(ThreeDLine start: self nearBottomLeft end: self farBottomLeft).
cachedLines add:
(ThreeDLine start: self nearTopRight end: self farTopRight).
cachedLines add:
(ThreeDLine start: self nearBottomRight end: self farBottomRight)].
^cachedLines!
asPlanes
"Answer with an OrderedCollection of ThreeDPlanes representing
the receiver."
| collection |
collection _ OrderedCollection new.
collection add: (ThreeDPlane
with: self farTopLeft with: self farBottomLeft
with: self nearBottomLeft with: self nearTopLeft).
collection add: (ThreeDPlane
with: self farTopLeft with: self farTopRight
with: self nearTopRight with: self nearTopLeft).
collection add: (ThreeDPlane
with: self farTopRight with: self farBottomRight
with: self nearBottomRight with: self nearTopRight).
collection add: (ThreeDPlane
with: self farBottomRight with: self farBottomLeft
with: self nearBottomLeft with: self nearBottomRight).
collection add: (ThreeDPlane
with: self farBottomRight with: self farTopRight
with: self farTopLeft with: self farBottomLeft).
collection add: (ThreeDPlane
with: self nearBottomRight with: self nearTopRight
with: self nearTopLeft with: self nearBottomLeft).
^collection! !
!Parallelepiped methodsFor: 'transforming'!
rotateBy: aRotation
"Answer a new Parallelepiped rotated by aRotation."
^Parallelepiped
origin: (origin rotateBy: aRotation)
horiz: (horiz rotateBy: aRotation)
vert: (vert rotateBy: aRotation)
depth: (depth rotateBy: aRotation)!
scaleBy: aThreeDPoint
"Answer a new Parallelepiped scaled by aThreeDPoint."
^Parallelepiped
origin: (origin scaleBy: aThreeDPoint)
horiz: (horiz scaleBy: aThreeDPoint)
vert: (vert scaleBy: aThreeDPoint)
depth: (depth scaleBy: aThreeDPoint)!
translateBy: aThreeDPoint
"Answer a new Parallelepiped translated by aThreeDPoint."
^Parallelepiped
origin: (origin translateBy: aThreeDPoint)
horiz: (horiz translateBy: aThreeDPoint)
vert: (vert translateBy: aThreeDPoint)
depth: (depth translateBy: aThreeDPoint)! !
!Parallelepiped methodsFor: 'private'!
nearestVerticesTo: vertex
"Answer with an OrderedCollection containing the three vertices
which are nearest to vertex."
(vertex = self nearTopLeft) ifTrue: [
^OrderedCollection
with: self nearTopRight
with: self nearBottomLeft
with: self farTopLeft].
(vertex = self nearTopRight) ifTrue: [
^OrderedCollection
with: self nearTopLeft
with: self nearBottomRight
with: self farTopRight].
(vertex = self nearBottomLeft) ifTrue: [
^OrderedCollection
with: self nearTopLeft
with: self nearBottomRight
with: self farBottomLeft].
(vertex = self nearBottomRight) ifTrue: [
^OrderedCollection
with: self nearTopRight
with: self nearBottomLeft
with: self farBottomRight].
(vertex = self farTopLeft) ifTrue: [
^OrderedCollection
with: self farTopRight
with: self farBottomLeft
with: self nearTopLeft].
(vertex = self farTopRight) ifTrue: [
^OrderedCollection
with: self farTopLeft
with: self farBottomRight
with: self nearTopRight].
(vertex = self farBottomLeft) ifTrue: [
^OrderedCollection
with: self farTopLeft
with: self farBottomRight
with: self nearBottomLeft].
^OrderedCollection
with: self farTopRight
with: self farBottomLeft
with: self nearBottomRight!
oppositeVertexTo: vertex
"Answer with the vertex directly opposite vertex."
(vertex = self nearTopLeft) ifTrue: [^self farBottomRight].
(vertex = self nearTopRight) ifTrue: [^self farBottomLeft].
(vertex = self nearBottomLeft) ifTrue: [^self farTopRight].
(vertex = self nearBottomRight) ifTrue: [^self farTopLeft].
(vertex = self farTopLeft) ifTrue: [^self nearBottomRight].
(vertex = self farTopRight) ifTrue: [^self nearBottomLeft].
(vertex = self farBottomLeft) ifTrue: [^self nearTopRight].
^self nearTopLeft! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
Parallelepiped class
instanceVariableNames: ''!
!Parallelepiped class methodsFor: 'instance creation'!
default
"The default Parallelepiped is a cuboid of size 3 by 4 by 5, centered
on the origin."
^self
origin: (ThreeDPoint x: -1.5 y: -2.0 z: -2.5)
horiz: (ThreeDPoint x: 1.5 y: -2.0 z: -2.5)
vert: (ThreeDPoint x: -1.5 y: 2.0 z: -2.5)
depth: (ThreeDPoint x: -1.5 y: -2.0 z: 2.5)!
origin: orig horiz: h vert: v depth: d
"Answer with a new instance of me with origin, and horizontal
and vertical sizes given by the arguments."
^self new origin: orig horiz: h vert: v depth: d!
origin: orig horizExtent: h vertExtent: v depthExtent: d
"Answer with a new instance of me with origin, and horizontal
and vertical extents given by the arguments."
^self new origin: orig horizExtent: h vertExtent: v depthExtent: d! !
Object subclass: #ThreeDTransformation
instanceVariableNames: 'scale translation rotation '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDTransformation comment:
'I represent the ability to perform transformations in 3-D space. My
protocols are modelled on those of class WindowingTransformation. My
instance variables are:
scale <Number> or <ThreeDPoint> representing a
linear scaling factor.
translation <Number> or <ThreeDPoint> representing a
translation in 3-D.
rotation <Array> of 9 numbers representing a rotation
in 3-D space.
All 3-D objects are supposed to be able to be transformed using
instances of me. Instances of me can also be combined to form a
single composite transformation.'!
!ThreeDTransformation methodsFor: 'accessing'!
rotation
"Answer a copy of the current rotation."
^rotation copy!
scale
"Answer a copy of the ThreeDPoint that represents the
current scale of the receiver."
scale == nil
ifTrue: [^ThreeDPoint x: 1 y: 1 z: 1]
ifFalse: [^scale copy]!
translation
"Answer a copy of the receiver's translation."
^translation copy!
translation: aValue
"Set the receiver's translation to aValue."
translation _ aValue! !
!ThreeDTransformation methodsFor: 'testing'!
noScale
"Answer true if the identity scale is in effect; answer false, otherwise."
^scale == nil! !
!ThreeDTransformation methodsFor: 'applying transform'!
applyInverseTo: anObject
"Apply the inverse of the receiver to anObject and answer the result."
| transformedObject |
transformedObject _ anObject translateBy: self inverseTranslation.
transformedObject _ transformedObject rotateBy: self inverseRotation.
scale == nil ifFalse: [
transformedObject _ transformedObject scaleBy: self inverseScale].
^transformedObject!
applyTo: anObject
"Apply the receiver to anObject and answer the result."
| transformedObject |
scale == nil
ifTrue: [transformedObject _ anObject]
ifFalse: [transformedObject _ anObject scaleBy: scale].
transformedObject _ transformedObject rotateBy: rotation.
transformedObject _ transformedObject translateBy: translation.
^transformedObject!
compose: aTransformation
"Answer a new ThreeDTransformation that is the
composition of the receiver and aTransformation.
The effect of applying the resulting ThreeDTransformation
to an object is the same as that of first applying
aTransformation to the object and then applying the
receiver to its result."
| aTransformationScale newScale newTranslation rot newRotation |
aTransformationScale _ aTransformation scale.
scale == nil
ifTrue:
[aTransformation noScale
ifTrue: [newScale _ nil]
ifFalse: [newScale _ aTransformationScale].
newTranslation _ translation + aTransformation translation]
ifFalse:
[aTransformation noScale
ifTrue: [newScale _ scale]
ifFalse: [newScale _ scale * aTransformationScale].
newTranslation _ translation + (scale * aTransformation translation)].
rot _ aTransformation rotation.
newRotation _ Array new: 9.
newRotation at: 1 put: ((rot at: 1) * (rotation at: 1)) +
((rot at: 2) * (rotation at: 4)) + ((rot at: 3) * (rotation at: 7)).
newRotation at: 2 put: ((rot at: 1) * (rotation at: 2)) +
((rot at: 2) * (rotation at: 5)) + ((rot at: 3) * (rotation at: 8)).
newRotation at: 3 put: ((rot at: 1) * (rotation at: 3)) +
((rot at: 2) * (rotation at: 6)) + ((rot at: 3) * (rotation at: 9)).
newRotation at: 4 put: ((rot at: 4) * (rotation at: 1)) +
((rot at: 5) * (rotation at: 4)) + ((rot at: 6) * (rotation at: 7)).
newRotation at: 5 put: ((rot at: 4) * (rotation at: 2)) +
((rot at: 5) * (rotation at: 5)) + ((rot at: 6) * (rotation at: 8)).
newRotation at: 6 put: ((rot at: 4) * (rotation at: 3)) +
((rot at: 5) * (rotation at: 6)) + ((rot at: 6) * (rotation at: 9)).
newRotation at: 7 put: ((rot at: 7) * (rotation at: 1)) +
((rot at: 8) * (rotation at: 4)) + ((rot at: 9) * (rotation at: 7)).
newRotation at: 8 put: ((rot at: 7) * (rotation at: 2)) +
((rot at: 8) * (rotation at: 5)) + ((rot at: 9) * (rotation at: 8)).
newRotation at: 9 put: ((rot at: 7) * (rotation at: 3)) +
((rot at: 8) * (rotation at: 6)) + ((rot at: 9) * (rotation at: 9)).
^ThreeDTransformation
scale: newScale
translation: newTranslation
rotation: newRotation! !
!ThreeDTransformation methodsFor: 'transforming'!
rotateBy: rot
"Answer with a new ThreeDTransformation rotated by rot."
| newRotation |
newRotation _ Array new: 9.
newRotation at: 1 put: ((rot at: 1) * (rotation at: 1)) +
((rot at: 2) * (rotation at: 4)) + ((rot at: 3) * (rotation at: 7)).
newRotation at: 2 put: ((rot at: 1) * (rotation at: 2)) +
((rot at: 2) * (rotation at: 5)) + ((rot at: 3) * (rotation at: 8)).
newRotation at: 3 put: ((rot at: 1) * (rotation at: 3)) +
((rot at: 2) * (rotation at: 6)) + ((rot at: 3) * (rotation at: 9)).
newRotation at: 4 put: ((rot at: 4) * (rotation at: 1)) +
((rot at: 5) * (rotation at: 4)) + ((rot at: 6) * (rotation at: 7)).
newRotation at: 5 put: ((rot at: 4) * (rotation at: 2)) +
((rot at: 5) * (rotation at: 5)) + ((rot at: 6) * (rotation at: 8)).
newRotation at: 6 put: ((rot at: 4) * (rotation at: 3)) +
((rot at: 5) * (rotation at: 6)) + ((rot at: 6) * (rotation at: 9)).
newRotation at: 7 put: ((rot at: 7) * (rotation at: 1)) +
((rot at: 8) * (rotation at: 4)) + ((rot at: 9) * (rotation at: 7)).
newRotation at: 8 put: ((rot at: 7) * (rotation at: 2)) +
((rot at: 8) * (rotation at: 5)) + ((rot at: 9) * (rotation at: 8)).
newRotation at: 9 put: ((rot at: 7) * (rotation at: 3)) +
((rot at: 8) * (rotation at: 6)) + ((rot at: 9) * (rotation at: 9)).
^ThreeDTransformation scale: scale translation: translation rotation: newRotation!
rotateXBy: anAngle
"Answer with a new ThreeDTransformation rotated about
the x-axis by anAngle."
| angleCos angleSin newRotation |
newRotation _ rotation deepCopy.
angleCos _ anAngle degreesToRadians cos.
angleSin _ anAngle degreesToRadians sin.
newRotation at: 4 put: ((rotation at: 4) * angleCos) + ((rotation at: 7) * angleSin).
newRotation at: 5 put: ((rotation at: 5) * angleCos) + ((rotation at: 8) * angleSin).
newRotation at: 6 put: ((rotation at: 6) * angleCos) + ((rotation at: 9) * angleSin).
angleSin _ 0 - angleSin.
newRotation at: 7 put: ((rotation at: 4) * angleSin) + ((rotation at: 7) * angleCos).
newRotation at: 8 put: ((rotation at: 5) * angleSin) + ((rotation at: 8) * angleCos).
newRotation at: 9 put: ((rotation at: 6) * angleSin) + ((rotation at: 9) * angleCos).
^ThreeDTransformation
scale: scale
translation: translation
rotation: newRotation!
rotateYBy: anAngle
"Answer with a new ThreeDTransformation rotated about
the y-axis by anAngle."
| angleCos angleSin newRotation |
newRotation _ rotation deepCopy.
angleCos _ anAngle degreesToRadians cos.
angleSin _ 0 - (anAngle degreesToRadians sin).
newRotation at: 1 put: ((rotation at: 1) * angleCos) + ((rotation at: 7) * angleSin).
newRotation at: 2 put: ((rotation at: 2) * angleCos) + ((rotation at: 8) * angleSin).
newRotation at: 3 put: ((rotation at: 3) * angleCos) + ((rotation at: 9) * angleSin).
angleSin _ 0 - angleSin.
newRotation at: 7 put: ((rotation at: 1) * angleSin) + ((rotation at: 7) * angleCos).
newRotation at: 8 put: ((rotation at: 2) * angleSin) + ((rotation at: 8) * angleCos).
newRotation at: 9 put: ((rotation at: 3) * angleSin) + ((rotation at: 9) * angleCos).
^ThreeDTransformation
scale: scale
translation: translation
rotation: newRotation!
rotateZBy: anAngle
"Answer with a new ThreeDTransformation rotated about
the z-axis by anAngle."
| angleCos angleSin newRotation |
newRotation _ rotation deepCopy.
angleCos _ anAngle degreesToRadians cos.
angleSin _ anAngle degreesToRadians sin.
newRotation at: 1 put: ((rotation at: 1) * angleCos) + ((rotation at: 4) * angleSin).
newRotation at: 2 put: ((rotation at: 2) * angleCos) + ((rotation at: 5) * angleSin).
newRotation at: 3 put: ((rotation at: 3) * angleCos) + ((rotation at: 6) * angleSin).
angleSin _ 0 - angleSin.
newRotation at: 4 put: ((rotation at: 1) * angleSin) + ((rotation at: 4) * angleCos).
newRotation at: 5 put: ((rotation at: 2) * angleSin) + ((rotation at: 5) * angleCos).
newRotation at: 6 put: ((rotation at: 3) * angleSin) + ((rotation at: 6) * angleCos).
^ThreeDTransformation
scale: scale
translation: translation
rotation: newRotation!
scaleBy: aScale
"Answer a new ThreeDTransformation with the scale and translation of
the receiver both scaled by aScale. Rotations are unaffected."
| checkedScale newScale newTranslation |
aScale == nil
ifTrue:
[newScale _ scale.
newTranslation _ translation]
ifFalse:
[checkedScale _ self checkScale: aScale.
scale == nil
ifTrue: [newScale _ checkedScale]
ifFalse: [newScale _ scale * checkedScale].
newTranslation _ checkedScale * translation].
^ThreeDTransformation
scale: newScale
translation: newTranslation
rotation: rotation!
translateBy: aThreeDPoint
"Answer a new ThreeDTransformation with the same scale and
rotations as the receiver and with a translation of the current
translation plus aThreeDPoint."
^ThreeDTransformation
scale: scale
translation: translation + aThreeDPoint
rotation: rotation! !
!ThreeDTransformation methodsFor: 'printing'!
printOn: aStream
aStream nextPutAll: self class name, ' scale: '.
scale printOn: aStream.
aStream nextPutAll: ' translation: '.
translation printOn: aStream.
aStream nextPutAll: ' rotation: '.
rotation printOn: aStream! !
!ThreeDTransformation methodsFor: 'private'!
checkScale: aScale
"Converts aScale to the internal format of a floating-point ThreeDPoint."
| checkedScale |
checkedScale _ aScale asThreeDPoint.
^ThreeDPoint
x: checkedScale x asFloat
y: checkedScale y asFloat
z: checkedScale x asFloat!
inverseRotation
"Answer with an Array representing the inverse of my rotation."
| invRotation |
invRotation _ Array new: 9.
invRotation at: 1 put: (rotation at: 1).
invRotation at: 2 put: (rotation at: 4).
invRotation at: 3 put: (rotation at: 7).
invRotation at: 4 put: (rotation at: 2).
invRotation at: 5 put: (rotation at: 5).
invRotation at: 6 put: (rotation at: 8).
invRotation at: 7 put: (rotation at: 3).
invRotation at: 8 put: (rotation at: 6).
invRotation at: 9 put: (rotation at: 9).
^invRotation!
inverseScale
"Answer with a ThreeDPoint representing the inverse of my
scale."
| newScale |
newScale _ self checkScale: scale.
^ThreeDPoint
x: (1.0 / newScale x)
y: (1.0 / newScale y)
z: (1.0 / newScale z)!
inverseTranslation
"Answer with a ThreeDPoint representing the inverse of my
translation."
| trans |
trans _ translation asThreeDPoint.
^ThreeDPoint
x: trans x negated
y: trans y negated
z: trans z negated!
setScale: aScale translation: aTranslation rotation: aRotation
"Sets the scale to aScale and the translation to aTranslation. Sets
the x,y and z rotations to aRotation."
scale _ aScale.
translation _ aTranslation.
rotation _ aRotation! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDTransformation class
instanceVariableNames: ''!
!ThreeDTransformation class methodsFor: 'instance creation'!
identity
"Answer an instance of me with no scaling (nil) and no translation
(0@0@0). All rotations are zero."
^self new
setScale: nil
translation: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
rotation: #(1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)!
rotation: aRotation
"Answer an instance of me with a scale factor of
nil and a translation offset of (0@0@0). Rotations
about the x, y and z axes are given by aRotation."
^self new
setScale: nil
translation: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
rotation: aRotation!
scale: aScale translation: aTranslation
"Answer an instance of me with a scale factor of
aScale and a translation offset of aTranslation. All
rotations are 0."
^self new
setScale: aScale
translation: aTranslation
rotation: #(1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)!
scale: aScale translation: aTranslation rotation: aRotation
"Answer an instance of me with a scale factor of
aScale and a translation offset of aTranslation. Rotations
about the x,y and z axes are given by aRotation."
^self new
setScale: aScale
translation: aTranslation
rotation: aRotation! !
ThreeDObject subclass: #ThreeDPlane
instanceVariableNames: 'vertices nearest furthest leftmost rightmost highest lowest '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDPlane comment:
'I represent a plane in 3-D. My instance variables are:
vertices <OrderedCollection> of ThreeDPoints, representing the edges
of the plane.
nearest <ThreeDPoint> representing the nearest point (i.e with the
smallest z coordinate).
furthest <ThreeDPoint> representing the furthest point.
leftmost <ThreeDPoint> representing the point furthest to the left (i.e with
the smallest x coordinate).
rightmost <ThreeDPoint> furthest to right.
highest <ThreeDPoint> representing uppermost point (i.e smallest y coordinate).
lowest <ThreeDPoint> furthest down.
The last six instance variables are used as cached versions, and are
calculated as required.'!
!ThreeDPlane methodsFor: 'initialize-release'!
initialize
vertices _ OrderedCollection new.! !
!ThreeDPlane methodsFor: 'accessing'!
furthestVertex
"Answer with the vertex with the largest z coordinate."
furthest == nil ifTrue: [^self computeFurthest] ifFalse: [^furthest]!
highestVertex
"Answer with the vertex with the smallest y coordinate."
highest == nil ifTrue: [^self computeHighest] ifFalse: [^highest]!
leftmostVertex
"Answer with the vertex with the smallest x coordinate."
leftmost == nil ifTrue: [^self computeLeftmost] ifFalse: [^leftmost]!
lowestVertex
"Answer with the vertex with the largest y coordinate."
lowest == nil ifTrue: [^self computeLowest] ifFalse: [^lowest]!
nearestVertex
"Answer with the vertex with the smallest z coordinate."
nearest == nil ifTrue: [^self computeNearest] ifFalse: [^nearest]!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object.
In this case, it is the point nearest to the observer."
^self nearestVertex!
rightmostVertex
"Answer with the vertex with the largest x coordinate."
rightmost == nil ifTrue: [^self computeRightmost] ifFalse: [^rightmost]!
vertices
"Answer with an OrderedCollection of Points representing the receiver."
^vertices!
vertices: aCollection
"Answer with an OrderedCollection of Points representing the receiver."
vertices _ aCollection asOrderedCollection! !
!ThreeDPlane methodsFor: 'comparing'!
= aThreeDPlane
"Answer whether the receiver and aThreeDPlane are equal."
self species = aThreeDPlane species
ifTrue: [^vertices = aThreeDPlane vertices]
ifFalse: [^false]! !
!ThreeDPlane methodsFor: 'testing'!
xOverlap: aThreeDPlane
"Answer true if the x-extent of the receiver overlaps that
of aThreeDPlane, otherwise answer false."
^(self leftmostVertex x <= aThreeDPlane leftmostVertex x
and: [self rightmostVertex x >= aThreeDPlane leftmostVertex x])
or: [self leftmostVertex x <= aThreeDPlane rightmostVertex x
and: [self rightmostVertex x >= aThreeDPlane rightmostVertex x]]!
yOverlap: aThreeDPlane
"Answer true if the y-extent of the receiver overlaps that
of aThreeDPlane, otherwise answer false."
^(self highestVertex y <= aThreeDPlane highestVertex y
and: [self lowestVertex y >= aThreeDPlane highestVertex y])
or: [self highestVertex y <= aThreeDPlane lowestVertex y
and: [self lowestVertex y >= aThreeDPlane lowestVertex y]]!
zOverlap: aThreeDPlane
"Answer true if the z-extent of the receiver overlaps that
of aThreeDPlane, otherwise answer false."
^(self nearestVertex z <= aThreeDPlane nearestVertex z
and: [self furthestVertex z >= aThreeDPlane nearestVertex z])
or: [self nearestVertex z <= aThreeDPlane furthestVertex z
and: [self furthestVertex z >= aThreeDPlane furthestVertex z]]! !
!ThreeDPlane methodsFor: 'modifying'!
moveObject: vertex to: newPoint
"Move the entire object so that vertex is at newPoint."
| delta |
delta _ newPoint - vertex.
self vertices: (self vertices collect: [:each |
each moveTo: each + delta]).
self changed!
moveVertex: vertex to: newPoint
"Move this vertex to newPoint."
self vertices: (self vertices collect: [:each |
each == vertex ifTrue: [each moveTo: newPoint] ifFalse: [each]]).
self changed! !
!ThreeDPlane methodsFor: 'truncation and rounding'!
rounded
"Answer with a new ThreeDPlane with all the vertices rounded."
^ThreeDPlane vertices: (self vertices collect: [:each | each rounded])!
truncated
"Answer with a new ThreeDPlane with all the vertices truncated."
^ThreeDPlane vertices: (self vertices collect: [:each | each truncated])! !
!ThreeDPlane methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection containing the lines representing the
edges of the receiver."
| array |
cachedLines isNil ifTrue: [
cachedLines _ OrderedCollection new.
array _ vertices asArray.
1 to: (array size - 1) do: [:each |
cachedLines add: (ThreeDLine
start: (array at: each)
end: (array at: each + 1))].
cachedLines add: (ThreeDLine start: array last end: array first)].
^cachedLines!
asPlanes
"Answer with an OrderedCollection containing the receiver. This
method is included for compatibility."
^OrderedCollection with: self! !
!ThreeDPlane methodsFor: 'transforming'!
rotateBy: aRotation
"Answer with a new ThreeDPlane rotated by aRotation."
^ThreeDPlane vertices: (vertices collect: [:each |
each rotateBy: aRotation])!
scaleBy: aThreeDPoint
"Answer with a new ThreeDPlane scaled by aThreeDPoint."
^ThreeDPlane vertices: (vertices collect: [:each |
each scaleBy: aThreeDPoint])!
translateBy: aThreeDPoint
"Answer with a new ThreeDPlane translated by aThreeDPoint."
^ThreeDPlane vertices: (vertices collect: [:each |
each translateBy: aThreeDPoint])! !
!ThreeDPlane methodsFor: 'printing'!
printOn: aStream
vertices printOn: aStream! !
!ThreeDPlane methodsFor: 'private'!
computeFurthest
"Compute the vertex with the largest z value."
furthest _ vertices first.
vertices do: [:eachVertex |
eachVertex z > furthest z ifTrue: [
furthest _ eachVertex]].
^furthest!
computeHighest
"Compute the vertex with the smallest y value."
highest _ vertices first.
vertices do: [:eachVertex |
eachVertex y < highest y ifTrue: [
highest _ eachVertex]].
^highest!
computeLeftmost
"Compute the vertex with the smallest x value."
leftmost _ vertices first.
vertices do: [:eachVertex |
eachVertex x < leftmost x ifTrue: [
leftmost _ eachVertex]].
^leftmost!
computeLowest
"Compute the vertex with the largest y value."
lowest _ vertices first.
vertices do: [:eachVertex |
eachVertex y > lowest y ifTrue: [
lowest _ eachVertex]].
^lowest!
computeNearest
"Compute the vertex with the smallest z value."
nearest _ vertices first.
vertices do: [:eachVertex |
eachVertex z < nearest z ifTrue: [
nearest _ eachVertex]].
^nearest!
computeRightmost
"Compute the vertex with the largest x value."
rightmost _ vertices first.
vertices do: [:eachVertex |
eachVertex x > rightmost x ifTrue: [
rightmost _ eachVertex]].
^rightmost! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDPlane class
instanceVariableNames: ''!
!ThreeDPlane class methodsFor: 'instance creation'!
vertices: aCollection
"Create a new ThreeDPlane with the contents of aCollection."
^self new vertices: (OrderedCollection new addAll: aCollection)!
with: firstPoint with: secondPoint
"Answer with a new ThreeDPlane described by firstPoint and secondPoint."
^self new vertices: (OrderedCollection with: firstPoint with: secondPoint)!
with: firstPoint with: secondPoint with: thirdPoint
"Answer with a new ThreeDPlane described by firstPoint, secondPoint
and thirdPoint."
^self new vertices:
(OrderedCollection
with: firstPoint
with: secondPoint
with: thirdPoint)!
with: firstPoint with: secondPoint with: thirdPoint with: fourthPoint
"Answer with a new ThreeDPlane described by firstPoint, secondPoint
thirdPoint and fourthPoint."
^self new vertices:
(OrderedCollection
with: firstPoint
with: secondPoint
with: thirdPoint
with: fourthPoint)! !
ThreeDObject subclass: #ThreeDModel
instanceVariableNames: 'objects '
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Graphics'!
ThreeDModel comment:
'I represent a collection of ThreeDObjects building up a scene. The
instance variable objects is an OrderedCollection of ThreeDObjects.'!
!ThreeDModel methodsFor: 'initialize-release'!
initialize
"Initialize the instance variable."
objects _ OrderedCollection new.!
release
"Send release to every object that I represent."
objects notNil ifTrue: [
objects do: [:eachObject | eachObject release]]! !
!ThreeDModel methodsFor: 'accessing'!
objects
"Answer with the OrderedCollection of ThreeDObjects
representing the receiver."
^objects!
objects: aCollection
"Set the OrderedCollection of ThreeDObjects
representing the receiver to aCollection."
objects _ aCollection!
refPoint
"Answer with a ThreeDPoint which is the 'reference point'
used when the object is first added to a compound object.
In this case, the reference point used is the reference point
of the first object in the receiver."
(objects size = 0)
ifTrue: [^ThreeDPoint x: 0.0 y: 0.0 z: 0.0]
ifFalse: [^objects first refPoint]!
vertices
"Answer with an OrderedCollection of vertices representing all the
ThreeDObjects in the receiver."
| collection |
collection _ OrderedCollection new.
objects do: [:eachObject | collection addAll: eachObject vertices].
^collection! !
!ThreeDModel methodsFor: 'comparing'!
= aThreeDModel
"Answer true if the receiver's species, apex and base match aThreeDModel's."
self species = aThreeDModel species
ifTrue: [^objects = aThreeDModel objects]
ifFalse: [^false]!
hash
^objects hash!
hashMappedBy: map
"Answer what my hash would be if oops changed according to map"
^ map newHashFor: self hash! !
!ThreeDModel methodsFor: 'modifying'!
addObject: anObject
"Add anObject to the collection of objects representing the receiver."
self objects add: anObject.
anObject addDependent: self.
cachedLines _ nil. "Model has changed."!
moveObject: vertex to: newPoint
"Move the entire object including vertex so that it is at newPoint."
| object |
object _ objects detect: [:eachObject |
eachObject includesVertex: vertex] ifNone: [^nil].
cachedLines _ nil. "Model has changed."
object moveObject: vertex to: newPoint.!
moveVertex: vertex to: newPoint
"Identify the object containing vertex. Move the vertex to newPoint."
| object |
object _ objects detect: [:eachObject |
eachObject includesVertex: vertex] ifNone: [^nil].
cachedLines _ nil. "Model has changed."
object moveVertex: vertex to: newPoint!
removeObject: vertex
"Remove the entire object containing vertex."
| object |
object _ objects detect: [:eachObject |
eachObject includesVertex: vertex] ifNone: [^nil].
objects remove: object.
object removeDependent: self.
self changed!
update: aParameter
self changed: aParameter! !
!ThreeDModel methodsFor: 'truncation and rounding'!
rounded
"Answer with a ThreeDModel which all of the objects rounded."
^ThreeDModel objects: (objects collect: [:eachObject | eachObject rounded])!
truncated
"Answer with a ThreeDModel which all of the objects truncated."
^ThreeDModel objects: (objects collect: [:eachObject | eachObject truncated])! !
!ThreeDModel methodsFor: 'converting'!
asLines
"Answer with an OrderedCollection of ThreeDLines representing all
of the ThreeDObjects in the receiver."
cachedLines _ OrderedCollection new.
objects do: [:eachObject | cachedLines addAll: eachObject asLines].
^cachedLines!
asPlanes
"Answer with an OrderedCollection of ThreeDPlanes representing all
of the ThreeDObjects in the receiver."
| collection |
collection _ OrderedCollection new.
objects do: [:eachObject | collection addAll: eachObject asPlanes].
^collection! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDModel class
instanceVariableNames: ''!
!ThreeDModel class methodsFor: 'instance creation'!
default
"The default ThreeDModel has no objects in it."
^self new!
new
"Create an initialized instance of the receiver."
^super new initialize!
objects: aCollection
"Create a new instance of me containing the objects in aCollection."
| newModel |
newModel _ self new.
aCollection do: [:eachObject |
newModel addObject: eachObject].
^newModel!
with: anObject
"Answer with a new instance of the receiver containing anObject."
^self objects: (OrderedCollection with: anObject)!
with: firstObject with: secondObject
"Answer with a new instance of the receiver containing firstObject
and secondObject."
^self objects: (OrderedCollection with: firstObject with: secondObject)!
with: firstObject with: secondObject with: thirdObject
"Answer with a new instance of the receiver containing firstObject,
secondObject and thirdObject."
^self objects:
(OrderedCollection
with: firstObject
with: secondObject
with: thirdObject)! !
MouseMenuController subclass: #ThreeDController
instanceVariableNames: 'redButtonFunction '
classVariableNames: 'DefaultRedButtonFunction ThreeDYellowButtonMenu ThreeDYellowButtonMessages '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDController methodsFor: 'initialize-release'!
initialize
"Initialize the yellow button menus and the red button operation."
super initialize.
self
yellowButtonMenu: ThreeDYellowButtonMenu
yellowButtonMessages: ThreeDYellowButtonMessages.
redButtonFunction _ DefaultRedButtonFunction.! !
!ThreeDController methodsFor: 'menu messages'!
addLine
"Set the current red button operation to be add line."
redButtonFunction _ #addLine!
addObject
"Prompt the user for the name of a new object. Add it to the model's
collection of objects."
| aName aSymbol newObject newPoint |
aName _ FillInTheBlank
request: 'Name of Three-D Object?'
initialAnswer: 'Cone'.
aName isEmpty ifTrue: [^nil].
aSymbol _ Smalltalk at: aName asSymbol ifAbsent: [
^self error: aName,' does not exist'].
(aSymbol isKindOf: Class)
ifTrue: [newObject _ aSymbol default]
ifFalse: [(aSymbol isKindOf: ThreeDObject)
ifTrue: [newObject _ aSymbol]
ifFalse: [^self error: aName,' is not a Three-D Object']].
newPoint _ self view currentTransformation applyInverseTo:
((self view inverseDisplayTransform:
(self view insetDisplayBox center)) asThreeDPoint).
newObject moveObject: newObject refPoint to: newPoint.
self model addObject: newObject.
self model changed!
addPlane
"Set the current red button operation to be add plane."
redButtonFunction _ #addPlane!
moveObject
"Set the current red button operation to be move object."
redButtonFunction _ #moveObject!
moveVertex
"Set the current red button operation to be move vertex."
redButtonFunction _ #moveVertex!
removeObject
"Set the current red button operation to be remove object."
redButtonFunction _ #removeObject! !
!ThreeDController methodsFor: 'control defaults'!
isControlActive
^(view containsPoint: sensor cursorPoint) & sensor blueButtonPressed not! !
!ThreeDController methodsFor: 'button activities'!
action: aSymbol at: aPoint
"Perform the action indicated by aSymbol at aPoint."
aSymbol == #moveVertex ifTrue: [^self moveVertexAt: aPoint].
aSymbol == #moveObject ifTrue: [^self moveObjectAt: aPoint].
aSymbol == #removeObject ifTrue: [^self removeObjectAt: aPoint].
aSymbol == #addLine ifTrue: [^self addLineAt: aPoint].
aSymbol == #addPlane ifTrue: [^self addPlaneAt: aPoint].!
addLineAt: aPoint
"Add a ThreeDLine to the model starting at aPoint."
| startPoint oldCursorPoint newCursorPoint newLine |
startPoint _ (self view inverseDisplayTransform: aPoint) asThreeDPoint.
oldCursorPoint _ aPoint.
newLine _ ThreeDLine start: startPoint end: startPoint.
self view displayLine: newLine.
[sensor redButtonPressed] whileTrue: [
newCursorPoint _ sensor cursorPoint.
newCursorPoint = oldCursorPoint ifFalse: [
self view displayLine: newLine.
newLine end: (self view inverseDisplayTransform: newCursorPoint) asThreeDPoint.
self view displayLine: newLine.
oldCursorPoint _ newCursorPoint]].
(newLine start = newLine end) ifFalse: [
self model addObject:
(self view currentTransformation applyInverseTo: newLine)]!
addPlaneAt: aPoint
"Add a ThreeDPlane to the model starting at aPoint."
| points endPoint oldCursorPoint newCursorPoint |
endPoint _ (self view inverseDisplayTransform: aPoint) asThreeDPoint.
points _ OrderedCollection with: endPoint.
oldCursorPoint _ aPoint.
self displayPolygon: points with: endPoint.
[sensor yellowButtonPressed] whileFalse: [
(sensor redButtonPressed)
ifTrue: [
newCursorPoint _ sensor cursorPoint.
newCursorPoint = oldCursorPoint ifFalse: [
self displayPolygon: points with: endPoint.
endPoint _ (self view inverseDisplayTransform: newCursorPoint) asThreeDPoint.
self displayPolygon: points with: endPoint.
oldCursorPoint _ newCursorPoint]]
ifFalse: [
points addLast: endPoint.
[sensor redButtonPressed or:
[sensor yellowButtonPressed]] whileFalse.
(points size = 2) ifTrue: [
self view displayFrom: points first to: points last]]].
self model addObject: (ThreeDPlane vertices:
(points collect: [:eachPoint |
self view currentTransformation applyInverseTo: eachPoint])).
self model changed.
[sensor yellowButtonPressed] whileTrue.!
moveObjectAt: aPoint
"Identify the nearest vertex to aPoint. Move the entire object
with the cursor point."
| vertex oldCursorPoint newCursorPoint newPoint |
vertex _ self view findNearestVertexTo: aPoint.
oldCursorPoint _ aPoint.
vertex notNil ifTrue: [[sensor redButtonPressed] whileTrue: [
newCursorPoint _ sensor cursorPoint.
newCursorPoint = oldCursorPoint ifFalse: [
newPoint _ (self view inverseDisplayTransform: newCursorPoint)
asThreeDPoint.
newPoint z: (self view currentTransformation applyTo: vertex) z.
self model
moveObject: vertex
to: (self view currentTransformation applyInverseTo: newPoint).
oldCursorPoint _ newCursorPoint]]]!
moveVertexAt: aPoint
"Identify the nearest vertex to aPoint. Move the identified vertex
with the cursor point."
| vertex oldCursorPoint newCursorPoint newPoint |
vertex _ self view findNearestVertexTo: aPoint.
oldCursorPoint _ aPoint.
vertex notNil ifTrue: [[sensor redButtonPressed] whileTrue: [
newCursorPoint _ sensor cursorPoint.
newCursorPoint = oldCursorPoint ifFalse: [
newPoint _ (self view inverseDisplayTransform: newCursorPoint)
asThreeDPoint.
newPoint z: (self view currentTransformation applyTo: vertex) z.
self model
moveVertex: vertex
to: (self view currentTransformation applyInverseTo: newPoint).
oldCursorPoint _ newCursorPoint]]]!
redButtonActivity
"Perform the current red button activity at the current input point."
sensor redButtonPressed ifTrue: [
self action: redButtonFunction at: sensor cursorPoint]!
removeObjectAt: aPoint
"Identify the nearest vertex to aPoint. Remove the entire object
pointed to."
| vertex |
vertex _ self view findNearestVertexTo: aPoint.
vertex notNil ifTrue: [self model removeObject: vertex]! !
!ThreeDController methodsFor: 'private'!
displayPolygon: aCollection with: aThreeDPoint
"Display the polygon represented by aCollection of
ThreeDPoints together with aThreeDPoint."
(aCollection size = 1)
ifTrue: [self view displayFrom: aCollection first to: aThreeDPoint]
ifFalse: [
self view displayFrom: aCollection first to: aThreeDPoint.
self view displayFrom: aCollection last to: aThreeDPoint].! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDController class
instanceVariableNames: ''!
!ThreeDController class methodsFor: 'class initialization'!
initialize
"Initialize the yellow button menu."
ThreeDYellowButtonMenu _ PopUpMenu
labels: 'move vertex\move object\remove object\add object\add line\add plane' withCRs
lines: #(3 4).
ThreeDYellowButtonMessages _ #(moveVertex moveObject removeObject addObject addLine addPlane).
"ThreeDController initialize."! !
ThreeDController initialize!
View subclass: #ThreeDView
instanceVariableNames: 'writePen currentTransformation angleStep scaleStep translationStep transformedVertices '
classVariableNames: 'DefaultAngleStep DefaultScaleStep DefaultTransformation DefaultTranslationStep DefaultWritePen '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDView methodsFor: 'initialize-release'!
initialize
"Initialize the instance variables"
super initialize.
writePen _ DefaultWritePen.
currentTransformation _ DefaultTransformation copy.
angleStep _ DefaultAngleStep.
scaleStep _ DefaultScaleStep.
translationStep _ DefaultTranslationStep! !
!ThreeDView methodsFor: 'accessing'!
angleStep
"Answer with the current value of the step used when rotating."
^angleStep!
angleStep: aNumber
"Set the current value of the step used when rotating."
angleStep _ aNumber!
currentTransformation
"Answer with the current ThreeDTransformation."
^currentTransformation!
findNearestVertexTo: aPoint
"Find the nearest vertex in the model to aPoint (which is in
absolute coordinates). Answer nil if no vertex is close enough,
otherwise answer with the selected vertex."
| vertices displayPoint nearby |
vertices _ self displayedVertices.
displayPoint _ self inverseDisplayTransform: aPoint.
nearby _ vertices select: [:each |
(each asPoint dist: displayPoint) < 20].
nearby size = 0 ifTrue: [^nil].
^self model findVertexNear: (currentTransformation applyInverseTo:
(nearby asSortedCollection: [:first :second |
(first asPoint dist: displayPoint) < (second asPoint dist: displayPoint)]) first)!
scaleStep
"Answer with the current value of the step used when scaling."
^scaleStep!
scaleStep: aNumber
"Set the current value of the step used when scaling."
scaleStep _ aNumber!
translationStep
"Answer with the current value of the step used when translating."
^translationStep!
translationStep: aNumber
"Set the current value of the step used when translating."
translationStep _ aNumber! !
!ThreeDView methodsFor: 'defaults accessing'!
defaultAngle
"Answer with the default angle of view."
^DefaultTransformation rotation deepCopy!
defaultAngleStep
"Answer with the default angle step."
^DefaultAngleStep!
defaultScale
"Answer with the default scale."
^DefaultTransformation scale deepCopy!
defaultScaleStep
"Answer with the default scale step."
^DefaultScaleStep!
defaultTranslation
"Answer with the default translation."
^DefaultTransformation translation deepCopy!
defaultTranslationStep
"Answer with the default translation step."
^DefaultTranslationStep! !
!ThreeDView methodsFor: 'button messages'!
fill
"Fill the planes represented by the model, starting at the back."
self depthSortFill: self displayedPlanes!
rotXneg
"Rotate the view about the X-axis by a negative amount."
currentTransformation _ currentTransformation rotateXBy: (0 - angleStep).
self displayView!
rotXpos
"Rotate the view about the X-axis by a positive amount."
currentTransformation _ currentTransformation rotateXBy: angleStep.
self displayView!
rotYneg
"Rotate the view about the Y-axis by a negative amount."
currentTransformation _ currentTransformation rotateYBy: (0 - angleStep).
self displayView!
rotYpos
"Rotate the view about the Y-axis by a positive amount."
currentTransformation _ currentTransformation rotateYBy: angleStep.
self displayView!
rotZneg
"Rotate the view about the Z-axis by a negative amount."
currentTransformation _ currentTransformation rotateZBy: (0 - angleStep).
self displayView!
rotZpos
"Rotate the view about the Z-axis by a positive amount."
currentTransformation _ currentTransformation rotateZBy: angleStep.
self displayView!
scaleLarger
"Scale the view in all axes to make it larger."
currentTransformation noScale ifFalse: [
currentTransformation _ currentTransformation scaleBy: scaleStep].
self displayView!
scaleSmaller
"Scale the view in all axes to make it smaller."
currentTransformation noScale ifFalse: [
currentTransformation _ currentTransformation scaleBy: (1.0 / scaleStep)].
self displayView!
setDefaultRotation
"Display the view using the default value of rotation."
currentTransformation _ ThreeDTransformation
scale: currentTransformation scale
translation: currentTransformation translation
rotation: self defaultAngle.
self displayView!
setDefaultScale
"Scale the view in all axes, using the default values."
currentTransformation noScale ifFalse: [
currentTransformation _ ThreeDTransformation
scale: self defaultScale
translation: currentTransformation translation
rotation: currentTransformation rotation].
self displayView!
setDefaultTranslation
"Display the view using the default value of translation."
currentTransformation _ ThreeDTransformation
scale: currentTransformation scale
translation: self defaultTranslation
rotation: currentTransformation rotation.
self displayView!
transXneg
"Translate the view in the X-axis negatively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp x: temp x - translationStep).
self displayView!
transXpos
"Translate the view in the X-axis positively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp x: temp x + translationStep).
self displayView!
transYneg
"Translate the view in the Y-axis negatively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp y: temp y - translationStep).
self displayView!
transYpos
"Translate the view in the Y-axis positively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp y: temp y + translationStep).
self displayView!
transZneg
"Translate the view in the Z-axis negatively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp z: temp z - translationStep).
self displayView!
transZpos
"Translate the view in the Z-axis positively."
| temp |
temp _ currentTransformation translation asThreeDPoint.
currentTransformation translation: (temp z: temp z + translationStep).
self displayView! !
!ThreeDView methodsFor: 'displaying'!
displayEdges: region
"Display the edges represented by region (an Array of Points)."
self
displayEdges: region
on: Display
offset: 0@0
clippingBox: self insetDisplayBox!
displayEdges: region on: aDisplayMedium
"Display the edges represented by region (an Array of Points)."
self displayEdges: region on: aDisplayMedium offset: 0@0!
displayEdges: region on: aDisplayMedium offset: aPoint
"Display the edges represented by region (an Array of Points)."
self
displayEdges: region
on: aDisplayMedium
offset: aPoint
clippingBox: aDisplayMedium boundingBox!
displayEdges: region on: aDisplayMedium offset: aPoint clippingBox: aRectangle
"Display the edges represented by region (an Array of Points)."
| pen |
pen _ Pen new destForm: aDisplayMedium.
pen frame: aRectangle.
1 to: (region size - 1) do: [:i |
pen place: (region at: i) - aPoint.
pen goto: (region at: i + 1) - aPoint].
pen place: (region last) - aPoint.
pen goto: (region first) - aPoint!
displayFrom: start to: end
"Display, using reverse mode, a line from start to end
(which are both ThreeDPoints)."
| pen |
pen _ Pen new frame: self insetDisplayBox.
pen combinationRule: Form reverse.
pen destForm: Display.
pen place: (self transformForDisplay: start).
pen goto: (self transformForDisplay: end)!
displayLine: aThreeDLine
"Display, using reverse mode, aThreeDLine."
| pen |
pen _ Pen new frame: self insetDisplayBox.
pen combinationRule: Form reverse.
pen destForm: Display.
pen place: (self transformForDisplay: aThreeDLine start).
pen goto: (self transformForDisplay: aThreeDLine end)!
displayLines: lines
"Display the lines (an OrderedCollection)."
| start end |
writePen frame: self insetDisplayBox.
lines do: [:each |
start _ self transformForDisplay: each start.
end _ self transformForDisplay: each end.
(self preClipFrom: start to: end) ifFalse: [
writePen place: start.
writePen goto: end]]!
displayPlane: aThreeDPlane
"Display aThreeDPlane on the view."
| size region form opaqueForm |
size _ aThreeDPlane vertices size.
(size < 2) ifTrue: [^self].
region _ (aThreeDPlane vertices collect: [:eachVertex |
self transformForDisplay: eachVertex]) asArray.
(size = 2) ifTrue: [^self displayEdges: region].
form _ Form fromRectangle: (self insetDisplayBox expandBy: 100).
self displayEdges: region on: form offset: form offset clippingBox: Display boundingBox.
form convexShapeFill: Form black.
opaqueForm _ OpaqueForm shape: form.
opaqueForm
displayOn: Display
at: 0@0
clippingBox: self insetDisplayBox
rule: Form over
mask: Form veryLightGray.
self displayEdges: region.!
displayView
"Remove the currently displayed lines. Calculate the
new lines according to currentTransformation
(a ThreeDTransformation), and display them."
| lines |
lines _ self displayedLines.
self clearInside.
self displayLines: lines!
update: aParameter
"Ignore aParameter, and update the display."
self topView isCollapsed ifFalse: [
self displaySafe: [self displayView]]! !
!ThreeDView methodsFor: 'filling'!
depthSortFill: planes
"Depth-sort and fill the planes (an OrderedCollection)."
| sortedPlanes eachPlane |
planes size = 0 ifTrue: [^self].
sortedPlanes _ self sortPlanes: planes.
sortedPlanes size timesRepeat: [
eachPlane _ sortedPlanes removeFirst.
(self displayPlane: eachPlane inFrontOf: sortedPlanes) ifFalse: [
sortedPlanes addLast: eachPlane]].
sortedPlanes do: [:each |
self displayPlane: each].!
displayPlane: aPlane inFrontOf: otherPlanes
"Display aPlane 'in front' of the planes in the OrderedCollection
otherPlanes, if possible. Answer true if aPlane was displayed,
otherwise false."
| zOverlaps xOverlaps yOverlaps c1 d1 c2 d2 |
zOverlaps _ self overlappingZ: aPlane with: otherPlanes.
zOverlaps size = 0 ifTrue: [self displayPlane: aPlane. ^true].
xOverlaps _ self overlappingX: aPlane with: zOverlaps.
xOverlaps size = 0 ifTrue: [self displayPlane: aPlane. ^true].
yOverlaps _ self overlappingY: aPlane with: xOverlaps.
yOverlaps size = 0 ifTrue: [self displayPlane: aPlane. ^true].
c1 _ yOverlaps select: [:eachPlane |
d1 _ eachPlane vertices select: [:eachVertex |
self dotProduct: aPlane with: eachVertex].
d1 size = eachPlane vertices size].
c1 size = yOverlaps size ifTrue: [self displayPlane: aPlane. ^true].
c2 _ yOverlaps select: [:eachPlane |
d2 _ aPlane vertices select: [:eachVertex |
self dotProduct: eachPlane with: eachVertex].
d2 size = aPlane vertices size].
c2 size = yOverlaps size ifTrue: [self displayPlane: aPlane. ^true].
^false!
dotProduct: aPlane with: aPoint
"Answer true if the dot products of every vertex in
aPlane with aPoint has the same sign, otherwise false."
| count |
count _ 0.
aPlane vertices do: [:eachVertex |
count _ count + (eachVertex dotProduct: aPoint) signPositive].
^(count abs = aPlane size)!
overlappingX: aThreeDPlane with: aCollection
"Answer with a collection of ThreeDPlanes which overlap in
the X direction with aThreeDPlane."
| newCollection |
newCollection _ aCollection select: [:eachPlane | eachPlane xOverlap: aThreeDPlane].
^newCollection!
overlappingY: aThreeDPlane with: aCollection
"Answer with a collection of ThreeDPlanes which overlap in
the Y direction with aThreeDPlane."
| newCollection |
newCollection _ aCollection select: [:eachPlane | eachPlane yOverlap: aThreeDPlane].
^newCollection!
overlappingZ: aThreeDPlane with: aCollection
"Answer with a collection of ThreeDPlanes which overlap in
the Z direction with aThreeDPlane."
| newCollection |
newCollection _ aCollection select: [:eachPlane | aThreeDPlane zOverlap: eachPlane].
^newCollection!
sortPlanes: planes
"Answer with an OrderedCollection of ThreeDPlanes
taken from planes which has been sorted in order
of the furthest vertex."
^(planes asSortedCollection: [:first :second |
first furthestVertex z > second furthestVertex z]) asOrderedCollection! !
!ThreeDView methodsFor: 'controller access'!
defaultControllerClass
^ThreeDController! !
!ThreeDView methodsFor: 'private'!
displayedLines
"Answer with an OrderedCollection of transformed ThreeDLines
representing the receiver's model."
| vertices |
vertices _ IdentityDictionary new.
^self model asLines collect: [:eachLine |
ThreeDLine
start: (vertices
at: (eachLine start)
addIfAbsent: [currentTransformation applyTo: eachLine start])
end: (vertices
at: (eachLine end)
addIfAbsent: [currentTransformation applyTo: eachLine end])]!
displayedPlanes
"Answer with an OrderedCollection of transformed ThreeDPlanes
representing the receiver's model."
^self model asPlanes collect: [:each |
currentTransformation applyTo: each]!
displayedVertices
"Answer with an OrderedCollection of transformed ThreeDPoints
representing the receiver's model."
^self model vertices collect: [:each |
currentTransformation applyTo: each]!
preClipFrom: start to: end
"Answer true if the line from start to end is completely to
the left, right top or bottom of the display box, otherwise false."
| left right top bottom |
left _ self insetDisplayBox left.
(start x <= left and: [end x <= left]) ifTrue: [^true].
right _ self insetDisplayBox right.
(start x >= right and: [end x >= right]) ifTrue: [^true].
top _ self insetDisplayBox top.
(start y <= top and: [end y <= top]) ifTrue: [^true].
bottom _ self insetDisplayBox bottom.
^(start y >= bottom and: [end y >= bottom])!
transformForDisplay: aThreeDPoint
"Answer with the Point in display coordinates corresponding
to aThreeDPoint."
^(self displayTransformation applyTo: aThreeDPoint asPoint) rounded! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDView class
instanceVariableNames: ''!
!ThreeDView class methodsFor: 'instance creation'!
open
"Create and schedule a new instance of me on the default
ThreeDModel."
"ThreeDView open."
self openOn: ThreeDModel default!
openOn: aModel
"Create and schedule a new instance of me on aModel."
"ThreeDView openOn: (ThreeDModel with: Cone default)."
| topView graphView buttonView scaleView vectorView fillView |
topView _ StandardSystemView
model: nil
label: 'Three-D Graphics'
minimumSize: 390@546.
topView borderWidth: 2.
topView window: (-1@-1 corner: 101@141).
graphView _ self new model: aModel.
graphView borderWidth: 2.
graphView insideColor: Form white.
graphView window: (-100@-100 corner: 100@100).
vectorView _ UnitVectorView new.
topView addSubView: vectorView viewport: (79@119 corner: 100@140).
buttonView _ ThreeDButtonView new model:
(OrderedCollection with: graphView with: vectorView).
topView addSubView: buttonView viewport: (0@101 corner: 65@140).
scaleView _ ThreeDScaleView new model: graphView.
topView addSubView: scaleView viewport: (66@101 corner: 100@118).
fillView _ ThreeDFillView new model: graphView.
topView addSubView: fillView viewport: (66@119 corner: 78@140).
topView addSubView: graphView viewport: (0@0 corner: 100@100).
topView controller open! !
!ThreeDView class methodsFor: 'class initialization'!
initialize
"Initialize various default values."
"ThreeDView initialize."
DefaultWritePen _ Pen new.
DefaultWritePen combinationRule: Form over.
DefaultAngleStep _ 5. "Eighteen steps to a quadrant."
DefaultScaleStep _ 1.414. "Step by square root 2."
DefaultTranslationStep _ 10.
DefaultTransformation _ ThreeDTransformation
scale: 10.0
translation: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
rotation: #(1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)! !
ThreeDView initialize!
View subclass: #ThreeDFillView
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDFillView methodsFor: 'adding subviews'!
buildButtonViews
"Add the button to the receiver."
| button view |
button _ Button newOff onAction: [self model perform: #fill].
view _ SwitchView new model: button.
view borderWidth: 2.
view label: ('F' asText allBold asDisplayText).
view controller: IndicatorOnSwitchController new.
self addSubView: view viewport: (2@9 extent: 8@8).!
buildLabelViews
"All the label to the receiver."
| view |
view _ DisplayTextView new model: 'Fill' asDisplayText.
view controller: NoController new.
view centered.
view borderWidth: 2.
view insideColor: Form white.
self addSubView: view viewport: (1@1 extent: 10@6)!
buildSubViews
"Add all the buttons and labels to the receiver."
self buildButtonViews.
self buildLabelViews! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDFillView class
instanceVariableNames: ''!
!ThreeDFillView class methodsFor: 'instance creation'!
new
"Create a new instance of me."
| view |
view _ super new borderWidth: 2.
view insideColor: Form darkGray.
view window: (0@0 extent: 12@21).
view buildSubViews.
^view! !
View subclass: #ThreeDButtonView
instanceVariableNames: ''
classVariableNames: 'DownArrow InArrow LeftArrow OutArrow RightArrow RLeftArrow RRightArrow UpArrow '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDButtonView methodsFor: 'adding subviews'!
buildButtonViews
"Add all the buttons to the receiver."
| offsets labels actions aButton aSwitchView |
offsets _ OrderedCollection new.
#(9 20 31) do: [:y |
#(8 18 38 48) do: [:x | offsets addLast: x@y]].
offsets _ offsets asArray.
labels _ OrderedCollection new.
labels add: DownArrow. labels add: UpArrow.
2 timesRepeat: [labels add: LeftArrow. labels add: RightArrow].
labels add: DownArrow. labels add: UpArrow.
labels add: RLeftArrow. labels add: RRightArrow.
labels add: InArrow. labels add: OutArrow.
labels _ labels asArray.
actions _ #(
rotXneg rotXpos transXneg transXpos
rotYneg rotYpos transYpos transYneg
rotZpos rotZneg transZpos transZneg).
1 to: 12 do: [ :i |
aButton _ Button newOff.
aButton onAction: [self model do: [:each | each perform: (actions at: i)]].
aSwitchView _ SwitchView new model: aButton.
aSwitchView borderWidth: 2.
aSwitchView label: (labels at: i).
aSwitchView controller: RepeatSwitchController new.
self
addSubView: aSwitchView
viewport: ((offsets at: i) extent: 8@8)]!
buildLabelViews
"Add all the labels to the receiver."
| xView yView zView tView rView |
xView _ DisplayTextView new model: 'X' asDisplayText.
xView controller: NoController new.
xView centered.
xView borderWidth: 2.
xView insideColor: Form white.
self addSubView: xView viewport: (29@10 extent: 6@6).
yView _ DisplayTextView new model: 'Y' asDisplayText.
yView controller: NoController new.
yView centered.
yView borderWidth: 2.
yView insideColor: Form white.
self addSubView: yView viewport: (29@21 extent: 6@6).
zView _ DisplayTextView new model: 'Z' asDisplayText.
zView controller: NoController new.
zView centered.
zView borderWidth: 2.
zView insideColor: Form white.
self addSubView: zView viewport: (29@32 extent: 6@6).
rView _ DisplayTextView new model: 'Rotate' asDisplayText.
rView controller: NoController new.
rView centered.
rView borderWidth: 2.
rView insideColor: Form white.
self addSubView: rView viewport: (8@1 extent: 18@6).
tView _ DisplayTextView new model: 'Translate' asDisplayText.
tView controller: NoController new.
tView centered.
tView borderWidth: 2.
tView insideColor: Form white.
self addSubView: tView viewport: (38@1 extent: 18@6).!
buildSubViews
"Add all the buttons and labels to the receiver."
self buildButtonViews.
self buildLabelViews! !
!ThreeDButtonView methodsFor: 'controller access'!
defaultControllerClass
^ThreeDButtonController! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDButtonView class
instanceVariableNames: ''!
!ThreeDButtonView class methodsFor: 'instance creation'!
new
"Create a new instance of me."
| view |
view _ super new borderWidth: 2.
view insideColor: Form darkGray.
view window: (0@0 extent: 65@40).
view buildSubViews.
^view! !
!ThreeDButtonView class methodsFor: 'class initialization'!
initialize
"ThreeDButtonView initialize."
LeftArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 1 0 3 0 7 0 15 0 31 0 63 65504 127 65504 255 65504 511 65504 1023 65504 1023 65504 511 65504 255 65504 127 65504 63 0 31 0 15 0 7 0 3 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
RightArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 0 32768 0 49152 0 57344 0 61440 0 63488 1023 64512 1023 65024 1023 65280 1023 65408 1023 65472 1023 65472 1023 65408 1023 65280 1023 65024 0 64512 0 63488 0 61440 0 57344 0 49152 0 32768 0 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
UpArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 1 32768 3 49152 7 57344 15 61440 31 63488 63 64512 127 65024 255 65280 511 65408 1023 65472 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 15 63488 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
DownArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 31 61440 1023 65472 511 65408 255 65280 127 65024 63 64512 31 63488 15 61440 7 57344 3 49152 1 32768 0 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
InArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 49152 3 49152 3 49152 3 49152 2 16384 2 16384 2 16384 511 65408 483 51072 483 51072 511 65408 2 16384 2 16384 2 16384 3 49152 3 49152 3 49152 3 49152 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
OutArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 49152 7 57344 15 61440 15 61440 15 61440 15 61440 7 57344 3 49152 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
RLeftArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 0 128 0 448 0 992 0 1984 0 3968 0 7936 0 15872 0 31744 0 63488 1 61440 3 57344 1031 49152 1551 32768 1823 0 1982 0 2044 0 2040 0 2032 0 2040 0 2044 0 2046 0 2047 0 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
RRightArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 256 0 896 0 1984 0 992 0 496 0 248 0 124 0 62 0 31 0 15 32768 7 49152 3 57376 1 61536 0 63712 0 32224 0 16352 0 8160 0 4064 0 8160 0 16352 0 32736 0 65504 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.! !
ThreeDButtonView initialize!
MouseMenuController subclass: #ThreeDButtonController
instanceVariableNames: ''
classVariableNames: 'ButtonsYellowButtonMenu ButtonsYellowButtonMessages '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDButtonController methodsFor: 'initialize-release'!
initialize
"Initialize the yellow button menus."
super initialize.
self
yellowButtonMenu: ButtonsYellowButtonMenu
yellowButtonMessages: ButtonsYellowButtonMessages! !
!ThreeDButtonController methodsFor: 'menu messages'!
defaultRotation
"Reset the rotation to the default value."
self model do: [:eachView | eachView setDefaultRotation]!
defaultRotationStep
"Reset the rotation step factor to the default value."
self model do: [:eachView | eachView angleStep: eachView defaultAngleStep]!
defaultTranslation
"Reset the translation to the default value."
self model do: [:eachView | eachView setDefaultTranslation]!
defaultTranslationStep
"Reset the translation step factor to the default value."
self model do: [:eachView |
eachView translationStep: eachView defaultTranslationStep]!
rotateStep
"Prompt the user for a new value for the rotation step. Inform
the model of the step value."
| answer newStep |
answer _ FillInTheBlank request: ' New Rotation Step (degrees)? '
initialAnswer: self model first angleStep printString.
answer isEmpty ifFalse: [
newStep _ Number readFrom: (ReadStream on: answer).
self model do: [:eachView | eachView angleStep: newStep]]!
translateStep
"Prompt the user for a new value for the translation step. Inform
the model of the step value."
| answer newStep |
answer _ FillInTheBlank request: ' New Translation Step? '
initialAnswer: self model first translationStep printString.
answer isEmpty ifFalse: [
newStep _ Number readFrom: (ReadStream on: answer).
self model do: [:eachView | eachView translationStep: newStep]]! !
!ThreeDButtonController methodsFor: 'control defaults'!
isControlActive
^(view containsPoint: sensor cursorPoint) & sensor blueButtonPressed not! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDButtonController class
instanceVariableNames: ''!
!ThreeDButtonController class methodsFor: 'class initialization'!
initialize
"Initialize the yellow button menu."
ButtonsYellowButtonMenu _ PopUpMenu
labels:
'set rotate step
default rotation
default rotation step
set translate step
default translation
default translation step' lines: #(1 3 4).
ButtonsYellowButtonMessages _ #(rotateStep defaultRotation defaultRotationStep translateStep defaultTranslation defaultTranslationStep).
"ThreeDButtonController initialize."! !
ThreeDButtonController initialize!
ThreeDView subclass: #UnitVectorView
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'Three-D-Views'!
!UnitVectorView methodsFor: 'initialize-release'!
initialize
"Initialize the instance variables"
super initialize.
currentTransformation _ ThreeDTransformation
scale: nil
translation: (ThreeDPoint x: 0.0 y: 0.0 z: 0.0)
rotation: #(1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)! !
!UnitVectorView methodsFor: 'defaults accessing'!
defaultAngle
"Answer with the default angle of view."
^#(1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0)!
defaultScale
"Answer with the default scale."
^nil!
defaultTranslation
"Answer with the default translation."
^ThreeDPoint x: 0.0 y: 0.0 z: 0.0! !
!UnitVectorView methodsFor: 'button messages'!
scaleLarger
"Do nothing; the unit vector view should not be scaled."!
scaleSmaller
"Do nothing; the unit vector view should not be scaled."!
transXneg
"Do nothing; the unit vector view should not be translated."!
transXpos
"Do nothing; the unit vector view should not be translated."!
transYneg
"Do nothing; the unit vector view should not be translated."!
transYpos
"Do nothing; the unit vector view should not be translated."!
transZneg
"Do nothing; the unit vector view should not be translated."!
transZpos
"Do nothing; the unit vector view should not be translated."! !
!UnitVectorView methodsFor: 'displaying'!
displayLine: aLine label: aString
"Display the line aLine, labelled by aString."
| end |
end _ (self displayTransformation applyTo: aLine end asPoint) rounded.
writePen place: (self displayTransformation applyTo: aLine start asPoint) rounded.
writePen goto: end.
aString asDisplayText
displayOn: Display
at: end
clippingBox: self insetDisplayBox
rule: Form under
mask: Form black!
displayView
"Calculate the new lines according to the
currentTransformation and display them."
writePen frame: self insetDisplayBox.
self clearInside.
self displayLine: (currentTransformation applyTo: self model xLine) label: 'X'.
self displayLine: (currentTransformation applyTo: self model yLine) label: 'Y'.
self displayLine: (currentTransformation applyTo: self model zLine) label: 'Z'! !
!UnitVectorView methodsFor: 'controller access'!
defaultControllerClass
^NoController! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
UnitVectorView class
instanceVariableNames: ''!
!UnitVectorView class methodsFor: 'instance creation'!
new
"Create a new instance of me."
| view |
view _ super new model: UnitVector new.
view borderWidth: 2.
view insideColor: Form white.
view window: (-1.4@-1.4 corner: 1.4@1.4).
^view! !
View subclass: #ThreeDScaleView
instanceVariableNames: ''
classVariableNames: 'BiggerArrow SmallerArrow '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDScaleView methodsFor: 'adding subviews'!
buildButtonViews
"Add all the buttons to the receiver."
| leftButton rightButton leftView rightView |
leftButton _ Button newOff onAction: [self model perform: #scaleSmaller].
leftView _ SwitchView new model: leftButton.
leftView borderWidth: 2.
leftView label: SmallerArrow.
leftView controller: RepeatSwitchController new.
self addSubView: leftView viewport: (7@9 extent: 8@8).
rightButton _ Button newOff onAction: [self model perform: #scaleLarger].
rightView _ SwitchView new model: rightButton.
rightView borderWidth: 2.
rightView label: BiggerArrow.
rightView controller: RepeatSwitchController new.
self addSubView: rightView viewport: (18@9 extent: 8@8).!
buildLabelViews
"All the label to the receiver."
| view |
view _ DisplayTextView new model: 'Scale' asDisplayText.
view controller: NoController new.
view centered.
view borderWidth: 2.
view insideColor: Form white.
self addSubView: view viewport: (7@1 extent: 19@6).!
buildSubViews
"Add all the buttons and labels to the receiver."
self buildButtonViews.
self buildLabelViews! !
!ThreeDScaleView methodsFor: 'controller access'!
defaultControllerClass
^ThreeDScaleController! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDScaleView class
instanceVariableNames: ''!
!ThreeDScaleView class methodsFor: 'instance creation'!
new
"Create a new instance of me."
| view |
view _ super new borderWidth: 2.
view insideColor: Form darkGray.
view window: (0@0 extent: 34@18).
view buildSubViews.
^view! !
!ThreeDScaleView class methodsFor: 'class initialization'!
initialize
"ThreeDScaleView initialize."
BiggerArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 512 32768 1793 49152 896 57344 448 28672 224 14336 112 7168 56 3584 28 1792 14 896 7 448 3 32992 3 32992 7 448 14 896 28 1792 56 3584 112 7168 224 14336 448 28672 896 57344 1793 49152 512 32768 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.
SmallerArrow _ Form
extent: 32@32
fromArray: #( 0 0 0 0 0 0 0 0 0 0 1 64 3 32992 7 448 14 896 28 1792 56 3584 112 7168 224 14336 448 28672 896 57344 1793 49152 1793 49152 896 57344 448 28672 224 14336 112 7168 56 3584 28 1792 14 896 7 448 3 32992 1 64 0 0 0 0 0 0 0 0 0 0)
offset: 0@0.! !
ThreeDScaleView initialize!
MouseMenuController subclass: #ThreeDScaleController
instanceVariableNames: ''
classVariableNames: 'ScaleYellowButtonMenu ScaleYellowButtonMessages '
poolDictionaries: ''
category: 'Three-D-Views'!
!ThreeDScaleController methodsFor: 'initialize-release'!
initialize
"Initialize the yellow button menus."
super initialize.
self
yellowButtonMenu: ScaleYellowButtonMenu
yellowButtonMessages: ScaleYellowButtonMessages! !
!ThreeDScaleController methodsFor: 'menu messages'!
defaultScale
"Reset the scaling step factor to the default value."
self model setDefaultScale!
defaultStep
"Reset the scaling step factor to the default value."
self model scaleStep: self model defaultScaleStep!
scaleStep
"Prompt the user for a new value for the scaling step. Inform
the model of the step value."
| answer newStep |
answer _ FillInTheBlank request: ' New Scale Step factor? '
initialAnswer: self model scaleStep printString.
answer isEmpty ifFalse: [
newStep _ Number readFrom: (ReadStream on: answer).
self model scaleStep: newStep]! !
!ThreeDScaleController methodsFor: 'control defaults'!
isControlActive
^(view containsPoint: sensor cursorPoint) & sensor blueButtonPressed not! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
ThreeDScaleController class
instanceVariableNames: ''!
!ThreeDScaleController class methodsFor: 'class initialization'!
initialize
"Initialize the yellow button menu."
ScaleYellowButtonMenu _ PopUpMenu
labels: 'new scale step\default scale\default scale step' withCRs
lines: #(1).
ScaleYellowButtonMessages _ #(scaleStep defaultScale defaultStep).
"ThreeDScaleController initialize."! !
ThreeDScaleController initialize!